source: functions.c @ 6772d19

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 6772d19 was e488ec5, checked in by Nelson Elhage <nelhage@mit.edu>, 14 years ago
Merge branch 'security'
  • Property mode set to 100644
File size: 94.8 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
5#include <netinet/in.h>
6#include <string.h>
7#include <time.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/wait.h>
11#include <errno.h>
12#include <signal.h>
13#include "owl.h"
14#include "filterproc.h"
15
16char *owl_function_command(const char *cmdbuff)
17{
18  owl_function_debugmsg("executing command: %s", cmdbuff);
19  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
20                             owl_global_get_context(&g), cmdbuff);
21}
22
23char *owl_function_command_argv(const char *const *argv, int argc)
24{
25  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
26                                  owl_global_get_context(&g),
27                                  argv, argc);
28}
29
30void owl_function_command_norv(const char *cmdbuff)
31{
32  char *rv;
33  rv=owl_function_command(cmdbuff);
34  if (rv) owl_free(rv);
35}
36
37void owl_function_command_alias(const char *alias_from, const char *alias_to)
38{
39  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
40}
41
42const owl_cmd *owl_function_get_cmd(const char *name)
43{
44  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
45}
46
47void owl_function_show_commands(void)
48{
49  owl_list l;
50  owl_fmtext fm;
51
52  owl_fmtext_init_null(&fm);
53  owl_fmtext_append_bold(&fm, "Commands:  ");
54  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
55  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
56  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
57  owl_fmtext_append_normal(&fm, "\n");
58  owl_function_popless_fmtext(&fm);
59  owl_cmddict_namelist_cleanup(&l);
60  owl_fmtext_cleanup(&fm);
61}
62
63void owl_function_show_view(const char *viewname)
64{
65  const owl_view *v;
66  owl_fmtext fm;
67
68  /* we only have the one view right now */
69  v=owl_global_get_current_view(&g);
70  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
71    owl_function_error("No view named '%s'", viewname);
72    return;
73  }
74
75  owl_fmtext_init_null(&fm);
76  owl_view_to_fmtext(v, &fm);
77  owl_function_popless_fmtext(&fm);
78  owl_fmtext_cleanup(&fm);
79}
80
81void owl_function_show_styles(void) {
82  owl_list l;
83  owl_fmtext fm;
84
85  owl_fmtext_init_null(&fm);
86  owl_fmtext_append_bold(&fm, "Styles:\n");
87  owl_global_get_style_names(&g, &l);
88  owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe);
89  owl_fmtext_append_normal(&fm, "\n");
90  owl_function_popless_fmtext(&fm);
91  owl_list_cleanup(&l, owl_free);
92  owl_fmtext_cleanup(&fm);
93}
94
95char *owl_function_style_describe(const char *name) {
96  const char *desc;
97  char *s;
98  const owl_style *style;
99  style = owl_global_get_style_by_name(&g, name);
100  if (style) {
101    desc = owl_style_get_description(style);
102  } else {
103    desc = "???";
104  }
105  s = owl_sprintf("%-20s - %s%s", name, 
106                  0==owl_style_validate(style)?"":"[INVALID] ",
107                  desc);
108  return s;
109}
110
111char *owl_function_cmd_describe(const char *name)
112{
113  const owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
114  if (cmd) return owl_cmd_describe(cmd);
115  else return(NULL);
116}
117
118void owl_function_show_command(const char *name)
119{
120  owl_function_help_for_command(name);
121}
122
123void owl_function_show_license(void)
124{
125  const char *text;
126
127  text=""
128    "barnowl version " OWL_VERSION_STRING "\n"
129    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
130    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
131    "\n"
132    "Redistribution and use in source and binary forms, with or without\n"
133    "modification, are permitted provided that the following conditions are\n"
134    "met:\n"
135    "\n"
136    "   * Redistributions of source code must retain the above copyright\n"
137    "     notice, this list of conditions and the following disclaimer.\n"
138    "\n"
139    "   * Redistributions in binary form must reproduce the above copyright\n"
140    "     notice, this list of conditions and the following disclaimer in\n"
141    "     the documentation and/or other materials provided with the\n"
142    "     distribution.\n"
143    "\n"
144    "   * Redistributions in any form must be accompanied by information on\n"
145    "     how to obtain complete source code for the Owl software and any\n"
146    "     accompanying software that uses the Owl software. The source code\n"
147    "     must either be included in the distribution or be available for no\n"
148    "     more than the cost of distribution plus a nominal fee, and must be\n"
149    "     freely redistributable under reasonable conditions. For an\n"
150    "     executable file, complete source code means the source code for\n"
151    "     all modules it contains. It does not include source code for\n"
152    "     modules or files that typically accompany the major components of\n"
153    "     the operating system on which the executable file runs.\n"
154    "\n"
155    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
156    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
157    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
158    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
159    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
160    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
161    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
162    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
163    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
164    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
165    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
166  owl_function_popless_text(text);
167}
168
169void owl_function_show_quickstart(void)
170{
171    const char *message =
172    "Move between messages with the arrow keys, and press 'r' to reply.\n"
173    "For more info, press 'h' or visit http://barnowl.mit.edu/\n\n"
174#ifdef HAVE_LIBZEPHYR
175    "@b(Zephyr:)\n"
176    "To send a message to a user, type ':zwrite @b(username)'. You can also\n"
177    "press 'z' and then type the username. To subscribe to a class, type\n"
178    "':sub @b(class)', and then type ':zwrite -c @b(class)' to send.\n\n"
179#endif
180    "@b(AIM:)\n"
181    "Log in to AIM with ':aimlogin @b(screenname)'. Use ':aimwrite @b(screenname)',\n"
182    "or 'a' and then the screen name, to send someone a message.\n\n"
183    ;
184
185    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_quickstart")) {
186        char *perlquickstart = owl_perlconfig_execute("BarnOwl::Hooks::_get_quickstart()");
187        if (perlquickstart) {
188            char *result = owl_sprintf("%s%s", message, perlquickstart);
189            owl_function_adminmsg("BarnOwl Quickstart", result);
190            owl_free(result);
191            owl_free(perlquickstart);
192            return;
193        }
194    }
195    owl_function_adminmsg("BarnOwl Quickstart", message);
196}
197
198
199/* Create an admin message, append it to the global list of messages
200 * and redisplay if necessary.
201 */
202void owl_function_adminmsg(const char *header, const char *body)
203{
204  owl_message *m;
205
206  m=owl_malloc(sizeof(owl_message));
207  owl_message_create_admin(m, header, body);
208 
209  /* add it to the global list and current view */
210  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
211  owl_view_consider_message(owl_global_get_current_view(&g), m);
212
213  /* do followlast if necessary */
214  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
215
216  /* redisplay etc. */
217  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
218}
219
220/* Create an outgoing zephyr message and return a pointer to it.  Does
221 * not put it on the global queue, use owl_global_messagequeue_addmsg() for
222 * that.
223 */
224owl_message *owl_function_make_outgoing_zephyr(const owl_zwrite *z)
225{
226  owl_message *m;
227
228  /* create the message */
229  m=owl_malloc(sizeof(owl_message));
230  owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z));
231
232  return(m);
233}
234
235/* Create an outgoing AIM message, returns a pointer to the created
236 * message or NULL if we're not logged into AIM (and thus unable to
237 * create the message).  Does not put it on the global queue.  Use
238 * owl_global_messagequeue_addmsg() for that .
239 */
240owl_message *owl_function_make_outgoing_aim(const char *body, const char *to)
241{
242  owl_message *m;
243
244  /* error if we're not logged into aim */
245  if (!owl_global_is_aimloggedin(&g)) return(NULL);
246 
247  m=owl_malloc(sizeof(owl_message));
248  owl_message_create_aim(m,
249                         owl_global_get_aim_screenname(&g),
250                         to,
251                         body,
252                         OWL_MESSAGE_DIRECTION_OUT,
253                         0);
254  return(m);
255}
256
257/* Create an outgoing loopback message and return a pointer to it.
258 * Does not append it to the global queue, use
259 * owl_global_messagequeue_addmsg() for that.
260 */
261owl_message *owl_function_make_outgoing_loopback(const char *body)
262{
263  owl_message *m;
264
265  /* create the message */
266  m=owl_malloc(sizeof(owl_message));
267  owl_message_create_loopback(m, body);
268  owl_message_set_direction_out(m);
269
270  return(m);
271}
272
273void owl_function_start_edit_win(const char *line, void (*callback)(owl_editwin *), void *data, void (*cleanup)(void *))
274{
275  owl_editwin *e;
276  char *s;
277
278  /* create and setup the editwin */
279  e = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
280                                   owl_global_get_msg_history(&g));
281  owl_editwin_set_dotsend(e);
282  s = owl_sprintf("----> %s\n", line);
283  owl_editwin_set_locktext(e, s);
284  owl_free(s);
285
286  owl_editwin_set_cbdata(e, data, cleanup);
287  owl_editwin_set_callback(e, callback);
288  owl_global_push_context(&g, OWL_CTX_EDITMULTI, e, "editmulti", owl_global_get_typwin_window(&g));
289}
290
291static void owl_function_write_setup(const char *noun)
292{
293
294  if (!owl_global_get_lockout_ctrld(&g))
295    owl_function_makemsg("Type your %s below.  "
296                         "End with ^D or a dot on a line by itself."
297                         "  ^C will quit.", noun);
298  else
299    owl_function_makemsg("Type your %s below.  "
300                         "End with a dot on a line by itself.  ^C will quit.",
301                         noun);
302}
303
304void owl_function_zwrite_setup(owl_zwrite *z)
305{
306  /* send a ping if necessary */
307  if (owl_global_is_txping(&g)) {
308    owl_zwrite_send_ping(z);
309  }
310
311
312  owl_function_write_setup("zephyr");
313  owl_function_start_edit_win(z->zwriteline,
314                              &owl_callback_zwrite,
315                              z, (void(*)(void*))owl_zwrite_delete);
316}
317
318void owl_function_aimwrite_setup(const char *line)
319{
320  owl_function_write_setup("message");
321  owl_function_start_edit_win(line,
322                              &owl_callback_aimwrite,
323                              owl_strdup(line),
324                              owl_free);
325
326}
327
328void owl_function_loopwrite_setup(void)
329{
330  owl_function_write_setup("message");
331  owl_function_start_edit_win("loopwrite",
332                              &owl_callback_loopwrite,
333                              "loopwrite", NULL);
334}
335
336void owl_callback_zwrite(owl_editwin *e) {
337  owl_zwrite *z = owl_editwin_get_cbdata(e);
338  owl_function_zwrite(z, owl_editwin_get_text(e));
339}
340
341/* send, log and display an outgoing zephyr.  If 'msg' is NULL
342 * the message is expected to be set from the zwrite line itself
343 */
344void owl_function_zwrite(owl_zwrite *z, const char *msg)
345{
346  owl_message *m;
347
348  if(strcmp(z->cmd, "zcrypt") == 0) {
349    owl_function_zcrypt(z, msg);
350    return;
351  }
352
353  /* create the zwrite and send the message */
354  owl_zwrite_populate_zsig(z);
355  if (msg) {
356    owl_zwrite_set_message(z, msg);
357  }
358  owl_zwrite_send_message(z);
359  owl_function_makemsg("Waiting for ack...");
360
361  /* If it's personal */
362  if (owl_zwrite_is_personal(z)) {
363    /* create the outgoing message */
364    m=owl_function_make_outgoing_zephyr(z);
365
366    if (m) {
367      owl_global_messagequeue_addmsg(&g, m);
368    } else {
369      owl_function_error("Could not create outgoing zephyr message");
370    }
371  }
372}
373
374/* send, log and display an outgoing zcrypt zephyr.  If 'msg' is NULL
375 * the message is expected to be set from the zwrite line itself
376 */
377void owl_function_zcrypt(owl_zwrite *z, const char *msg)
378{
379  char *cryptmsg;
380  owl_message *m;
381  const char *argv[7];
382  char *zcrypt;
383  int rv, status;
384  char *old_msg;
385
386  /* create the zwrite and send the message */
387  owl_zwrite_populate_zsig(z);
388  if (msg) {
389    owl_zwrite_set_message(z, msg);
390  }
391  old_msg = owl_strdup(owl_zwrite_get_message(z));
392
393  zcrypt = owl_sprintf("%s/zcrypt", owl_get_bindir());
394  argv[0] = "zcrypt";
395  argv[1] = "-E";
396  argv[2] = "-c"; argv[3] = owl_zwrite_get_class(z);
397  argv[4] = "-i"; argv[5] = owl_zwrite_get_instance(z);
398  argv[6] = NULL;
399
400  rv = call_filter(zcrypt, argv, owl_zwrite_get_message(z), &cryptmsg, &status);
401
402  owl_free(zcrypt);
403
404  if (rv || status) {
405    if(cryptmsg) owl_free(cryptmsg);
406    owl_free(old_msg);
407    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
408    owl_function_beep();
409    return;
410  }
411
412  owl_zwrite_set_message_raw(z, cryptmsg);
413  owl_zwrite_set_opcode(z, "crypt");
414
415  owl_zwrite_send_message(z);
416  owl_function_makemsg("Waiting for ack...");
417
418  /* If it's personal */
419  if (owl_zwrite_is_personal(z)) {
420    /* Create the outgoing message. Restore the un-crypted message for display. */
421    owl_zwrite_set_message_raw(z, old_msg);
422    m=owl_function_make_outgoing_zephyr(z);
423    if (m) {
424      owl_global_messagequeue_addmsg(&g, m);
425    } else {
426      owl_function_error("Could not create outgoing zephyr message");
427    }
428  }
429
430  /* free the zwrite */
431  owl_free(cryptmsg);
432}
433
434void owl_callback_aimwrite(owl_editwin *e) {
435  char *command = owl_editwin_get_cbdata(e);
436  owl_function_aimwrite(command,
437                        owl_editwin_get_text(e));
438}
439
440void owl_function_aimwrite(const char *line, const char *msg)
441{
442  int ret;
443  const char *to;
444  char *format_msg;
445  owl_message *m;
446
447  to = line + 9;
448
449  /* make a formatted copy of the message */
450  format_msg=owl_strdup(msg);
451  owl_text_wordunwrap(format_msg);
452 
453  /* send the message */
454  ret=owl_aim_send_im(to, format_msg);
455  if (!ret) {
456    owl_function_makemsg("AIM message sent.");
457  } else {
458    owl_function_error("Could not send AIM message.");
459  }
460
461  /* create the outgoing message */
462  m=owl_function_make_outgoing_aim(msg, to);
463
464  if (m) {
465    owl_global_messagequeue_addmsg(&g, m);
466  } else {
467    owl_function_error("Could not create outgoing AIM message");
468  }
469
470  owl_free(format_msg);
471}
472
473void owl_function_send_aimawymsg(const char *to, const char *msg)
474{
475  int ret;
476  char *format_msg;
477  owl_message *m;
478
479  /* make a formatted copy of the message */
480  format_msg=owl_strdup(msg);
481  owl_text_wordunwrap(format_msg);
482 
483  /* send the message */
484  ret=owl_aim_send_awaymsg(to, format_msg);
485  if (!ret) {
486    /* owl_function_makemsg("AIM message sent."); */
487  } else {
488    owl_function_error("Could not send AIM message.");
489  }
490
491  /* create the message */
492  m=owl_function_make_outgoing_aim(msg, to);
493  if (m) {
494    owl_global_messagequeue_addmsg(&g, m);
495  } else {
496    owl_function_error("Could not create AIM message");
497  }
498  owl_free(format_msg);
499}
500
501void owl_callback_loopwrite(owl_editwin *e) {
502  owl_function_loopwrite(owl_editwin_get_text(e));
503}
504
505void owl_function_loopwrite(const char *msg)
506{
507  owl_message *min, *mout;
508
509  /* create a message and put it on the message queue.  This simulates
510   * an incoming message */
511  min=owl_malloc(sizeof(owl_message));
512  mout=owl_function_make_outgoing_loopback(msg);
513
514  if (owl_global_is_displayoutgoing(&g)) {
515    owl_global_messagequeue_addmsg(&g, mout);
516  } else {
517    owl_message_delete(mout);
518  }
519
520  owl_message_create_loopback(min, msg);
521  owl_message_set_direction_in(min);
522  owl_global_messagequeue_addmsg(&g, min);
523
524  /* fake a makemsg */
525  owl_function_makemsg("loopback message sent");
526}
527
528/* If filter is non-null, looks for the next message matching
529 * that filter.  If skip_deleted, skips any deleted messages.
530 * If last_if_none, will stop at the last message in the view
531 * if no matching messages are found.  */
532void owl_function_nextmsg_full(const char *filter, int skip_deleted, int last_if_none)
533{
534  int curmsg, i, viewsize, found;
535  const owl_view *v;
536  const owl_filter *f = NULL;
537  const owl_message *m;
538
539  v=owl_global_get_current_view(&g);
540
541  if (filter) {
542    f=owl_global_get_filter(&g, filter);
543    if (!f) {
544      owl_function_error("No %s filter defined", filter);
545      return;
546    }
547  }
548
549  curmsg=owl_global_get_curmsg(&g);
550  viewsize=owl_view_get_size(v);
551  found=0;
552
553  /* just check to make sure we're in bounds... */
554  if (curmsg>viewsize-1) curmsg=viewsize-1;
555  if (curmsg<0) curmsg=0;
556
557  for (i=curmsg+1; i<viewsize; i++) {
558    m=owl_view_get_element(v, i);
559    if (skip_deleted && owl_message_is_delete(m)) continue;
560    if (f && !owl_filter_message_match(f, m)) continue;
561    found = 1;
562    break;
563  }
564
565  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
566  if (i<0) i=0;
567
568  if (!found) {
569    owl_function_makemsg("already at last%s message%s%s%s",
570                         skip_deleted?" non-deleted":"",
571                         filter?" in ":"", filter?filter:"",
572                         owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g)) ?
573                         ", press Enter to scroll" : "");
574    /* if (!skip_deleted) owl_function_beep(); */
575  }
576
577  if (last_if_none || found) {
578    owl_global_set_curmsg(&g, i);
579    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
580    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
581    owl_global_set_direction_downwards(&g);
582  }
583}
584
585void owl_function_prevmsg_full(const char *filter, int skip_deleted, int first_if_none)
586{
587  int curmsg, i, found;
588  const owl_view *v;
589  const owl_filter *f = NULL;
590  const owl_message *m;
591
592  v=owl_global_get_current_view(&g);
593
594  if (filter) {
595    f=owl_global_get_filter(&g, filter);
596    if (!f) {
597      owl_function_error("No %s filter defined", filter);
598      return;
599    }
600  }
601
602  curmsg=owl_global_get_curmsg(&g);
603  found=0;
604
605  /* just check to make sure we're in bounds... */
606  if (curmsg<0) curmsg=0;
607
608  for (i=curmsg-1; i>=0; i--) {
609    m=owl_view_get_element(v, i);
610    if (skip_deleted && owl_message_is_delete(m)) continue;
611    if (f && !owl_filter_message_match(f, m)) continue;
612    found = 1;
613    break;
614  }
615
616  if (i<0) i=0;
617
618  if (!found) {
619    owl_function_makemsg("already at first%s message%s%s",
620                         skip_deleted?" non-deleted":"",
621                         filter?" in ":"", filter?filter:"");
622    /* if (!skip_deleted) owl_function_beep(); */
623  }
624
625  if (first_if_none || found) {
626    owl_global_set_curmsg(&g, i);
627    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
628    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
629    owl_global_set_direction_upwards(&g);
630  }
631}
632
633void owl_function_nextmsg(void)
634{
635  owl_function_nextmsg_full(NULL, 0, 1);
636}
637
638void owl_function_prevmsg(void)
639{
640  owl_function_prevmsg_full(NULL, 0, 1);
641}
642
643void owl_function_nextmsg_notdeleted(void)
644{
645  owl_function_nextmsg_full(NULL, 1, 1);
646}
647
648void owl_function_prevmsg_notdeleted(void)
649{
650  owl_function_prevmsg_full(NULL, 1, 1);
651}
652
653/* if move_after is 1, moves after the delete */
654void owl_function_deletecur(int move_after)
655{
656  int curmsg;
657  owl_view *v;
658
659  v=owl_global_get_current_view(&g);
660
661  /* bail if there's no current message */
662  if (owl_view_get_size(v) < 1) {
663    owl_function_error("No current message to delete");
664    return;
665  }
666
667  /* mark the message for deletion */
668  curmsg=owl_global_get_curmsg(&g);
669  owl_view_delete_element(v, curmsg);
670
671  if (move_after) {
672    /* move the poiner in the appropriate direction
673     * to the next undeleted msg */
674    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
675      owl_function_prevmsg_notdeleted();
676    } else {
677      owl_function_nextmsg_notdeleted();
678    }
679  }
680}
681
682void owl_function_undeletecur(int move_after)
683{
684  int curmsg;
685  owl_view *v;
686
687  v=owl_global_get_current_view(&g);
688 
689  if (owl_view_get_size(v) < 1) {
690    owl_function_error("No current message to undelete");
691    return;
692  }
693  curmsg=owl_global_get_curmsg(&g);
694
695  owl_view_undelete_element(v, curmsg);
696
697  if (move_after) {
698    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
699      if (curmsg>0) {
700        owl_function_prevmsg();
701      } else {
702        owl_function_nextmsg();
703      }
704    } else {
705      owl_function_nextmsg();
706    }
707  }
708
709  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
710}
711
712void owl_function_expunge(void)
713{
714  int curmsg;
715  const owl_message *m;
716  owl_messagelist *ml;
717  owl_view *v;
718  int lastmsgid=0;
719
720  curmsg=owl_global_get_curmsg(&g);
721  v=owl_global_get_current_view(&g);
722  ml=owl_global_get_msglist(&g);
723
724  m=owl_view_get_element(v, curmsg);
725  if (m) lastmsgid = owl_message_get_id(m);
726
727  /* expunge the message list */
728  owl_messagelist_expunge(ml);
729
730  /* update all views (we only have one right now) */
731  owl_view_recalculate(v);
732
733  /* find where the new position should be
734     (as close as possible to where we last where) */
735  curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid);
736  if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1;
737  if (curmsg<0) curmsg = 0;
738  owl_global_set_curmsg(&g, curmsg);
739  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
740  /* if there are no messages set the direction to down in case we
741     delete everything upwards */
742  owl_global_set_direction_downwards(&g);
743 
744  owl_function_makemsg("Messages expunged");
745  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
746}
747
748void owl_function_firstmsg(void)
749{
750  owl_global_set_curmsg(&g, 0);
751  owl_global_set_topmsg(&g, 0);
752  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
753  owl_global_set_direction_downwards(&g);
754}
755
756void owl_function_lastmsg_noredisplay(void)
757{
758  int oldcurmsg, curmsg;
759  const owl_view *v;
760
761  v=owl_global_get_current_view(&g);
762  oldcurmsg=owl_global_get_curmsg(&g);
763  curmsg=owl_view_get_size(v)-1; 
764  if (curmsg<0) curmsg=0;
765  owl_global_set_curmsg(&g, curmsg);
766  if (oldcurmsg < curmsg) {
767    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
768  } else if (curmsg<owl_view_get_size(v)) {
769    /* If already at the end, blank the screen and move curmsg
770     * past the end of the messages. */
771    owl_global_set_topmsg(&g, curmsg+1);
772    owl_global_set_curmsg(&g, curmsg+1);
773  } 
774  /* owl_mainwin_redisplay(owl_global_get_mainwin(&g)); */
775  owl_global_set_direction_downwards(&g);
776}
777
778void owl_function_lastmsg(void)
779{
780  owl_function_lastmsg_noredisplay();
781  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
782}
783
784void owl_function_shift_right(void)
785{
786  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
787}
788
789void owl_function_shift_left(void)
790{
791  int shift;
792
793  shift=owl_global_get_rightshift(&g);
794  if (shift > 0) {
795    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
796  } else {
797    owl_function_beep();
798    owl_function_makemsg("Already full left");
799  }
800}
801
802void owl_function_unsuball(void)
803{
804  unsuball();
805  owl_function_makemsg("Unsubscribed from all messages.");
806}
807
808
809/* Load zephyr subscriptions from the named 'file' and load zephyr's
810 * default subscriptions as well.  An error message is printed if
811 * 'file' can't be opened or if zephyr reports an error in
812 * subscribing.
813 *
814 * If 'file' is NULL, this look for the default filename
815 * $HOME/.zephyr.subs.  If the file can not be opened in this case
816 * only, no error message is printed.
817 */
818void owl_function_loadsubs(const char *file)
819{
820  int ret, ret2;
821  const char *foo;
822  char *path;
823
824  if (file==NULL) {
825    ret=owl_zephyr_loadsubs(NULL, 0);
826  } else {
827    path = owl_util_makepath(file);
828    ret=owl_zephyr_loadsubs(path, 1);
829    owl_free(path);
830  }
831
832  /* for backwards compatibility for now */
833  ret2=owl_zephyr_loaddefaultsubs();
834
835  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
836
837  foo=file?file:"file";
838  if (ret==0 && ret2==0) {
839    if (!file) {
840      owl_function_makemsg("Subscribed to messages.");
841    } else {
842      owl_function_makemsg("Subscribed to messages from %s", file);
843    }
844  } else if (ret==-1) {
845    owl_function_error("Could not read %s", foo);
846  } else {
847    owl_function_error("Error subscribing to messages");
848  }
849}
850
851void owl_function_loadloginsubs(const char *file)
852{
853  int ret;
854
855  ret=owl_zephyr_loadloginsubs(file);
856
857  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
858  if (ret==0) {
859  } else if (ret==-1) {
860    owl_function_error("Could not open file for login subscriptions.");
861  } else {
862    owl_function_error("Error subscribing to login messages from file.");
863  }
864}
865
866void owl_callback_aimlogin(owl_editwin *e) {
867  char *user = owl_editwin_get_cbdata(e);
868  owl_function_aimlogin(user,
869                        owl_editwin_get_text(e));
870}
871
872void owl_function_aimlogin(const char *user, const char *passwd) {
873  int ret;
874
875  /* clear the buddylist */
876  owl_buddylist_clear(owl_global_get_buddylist(&g));
877
878  /* try to login */
879  ret=owl_aim_login(user, passwd);
880  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
881}
882
883void owl_function_suspend(void)
884{
885  endwin();
886  printf("\n");
887  kill(getpid(), SIGSTOP);
888
889  /* resize to reinitialize all the windows when we come back */
890  owl_command_resize();
891}
892
893void owl_function_zaway_toggle(void)
894{
895  if (!owl_global_is_zaway(&g)) {
896    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
897    owl_function_zaway_on();
898  } else {
899    owl_function_zaway_off();
900  }
901}
902
903void owl_function_zaway_on(void)
904{
905  owl_global_set_zaway_on(&g);
906  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
907}
908
909void owl_function_zaway_off(void)
910{
911  owl_global_set_zaway_off(&g);
912  owl_function_makemsg("zaway off");
913}
914
915void owl_function_aaway_toggle(void)
916{
917  if (!owl_global_is_aaway(&g)) {
918    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
919    owl_function_aaway_on();
920  } else {
921    owl_function_aaway_off();
922  }
923}
924
925void owl_function_aaway_on(void)
926{
927  owl_global_set_aaway_on(&g);
928  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
929  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
930}
931
932void owl_function_aaway_off(void)
933{
934  owl_global_set_aaway_off(&g);
935  /* owl_aim_set_awaymsg(""); */
936  owl_function_makemsg("AIM away off");
937}
938
939void owl_function_quit(void)
940{
941  char *ret;
942 
943  /* zlog out if we need to */
944  if (owl_global_is_havezephyr(&g) &&
945      owl_global_is_shutdownlogout(&g)) {
946    owl_zephyr_zlog_out();
947  }
948
949  /* execute the commands in shutdown */
950  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
951  if (ret) owl_free(ret);
952
953  /* signal our child process, if any */
954  if (owl_global_get_newmsgproc_pid(&g)) {
955    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
956  }
957
958  /* Quit zephyr */
959  owl_zephyr_shutdown();
960 
961  /* Quit AIM */
962  if (owl_global_is_aimloggedin(&g)) {
963    owl_aim_logout();
964  }
965
966  /* done with curses */
967  endwin();
968
969  /* restore terminal settings */
970  tcsetattr(0, TCSAFLUSH, owl_global_get_startup_tio(&g));
971
972  owl_function_debugmsg("Quitting Owl");
973  exit(0);
974}
975
976void owl_function_calculate_topmsg(int direction)
977{
978  int recwinlines, topmsg, curmsg;
979  const owl_view *v;
980
981  v=owl_global_get_current_view(&g);
982  curmsg=owl_global_get_curmsg(&g);
983  topmsg=owl_global_get_topmsg(&g);
984  recwinlines=owl_global_get_recwin_lines(&g);
985
986  /*
987  if (owl_view_get_size(v) < 1) {
988    return;
989  }
990  */
991
992  switch (owl_global_get_scrollmode(&g)) {
993  case OWL_SCROLLMODE_TOP:
994    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
995    break;
996  case OWL_SCROLLMODE_NEARTOP:
997    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
998    break;
999  case OWL_SCROLLMODE_CENTER:
1000    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
1001    break;
1002  case OWL_SCROLLMODE_PAGED:
1003    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
1004    break;
1005  case OWL_SCROLLMODE_PAGEDCENTER:
1006    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
1007    break;
1008  case OWL_SCROLLMODE_NORMAL:
1009  default:
1010    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
1011  }
1012  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
1013  owl_global_set_topmsg(&g, topmsg);
1014}
1015
1016/* Returns what the new topmsg should be. 
1017 * Passed the last direction of movement,
1018 * the current view,
1019 * the current message number in the view,
1020 * the top message currently being displayed,
1021 * and the number of lines in the recwin.
1022 */
1023int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1024{
1025  return(curmsg);
1026}
1027
1028int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1029{
1030  if (curmsg>0 
1031      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1032          <  recwinlines/2)) {
1033    return(curmsg-1);
1034  } else {
1035    return(curmsg);
1036  }
1037}
1038 
1039int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1040{
1041  int i, last, lines;
1042
1043  last = curmsg;
1044  lines = 0;
1045  for (i=curmsg-1; i>=0; i--) {
1046    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1047    if (lines > recwinlines/2) break;
1048    last = i;
1049  }
1050  return(last);
1051}
1052 
1053int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
1054{
1055  int i, last, lines, savey;
1056 
1057  /* If we're off the top of the screen, scroll up such that the
1058   * curmsg is near the botton of the screen. */
1059  if (curmsg < topmsg) {
1060    last = curmsg;
1061    lines = 0;
1062    for (i=curmsg; i>=0; i--) {
1063      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1064      if (lines > recwinlines) break;
1065    last = i;
1066    }
1067    if (center_on_page) {
1068      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1069    } else {
1070      return(last);
1071    }
1072  }
1073
1074  /* Find number of lines from top to bottom of curmsg (store in savey) */
1075  savey=0;
1076  for (i=topmsg; i<=curmsg; i++) {
1077    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1078  }
1079
1080  /* if we're off the bottom of the screen, scroll down */
1081  if (savey > recwinlines) {
1082    if (center_on_page) {
1083      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1084    } else {
1085      return(curmsg);
1086    }
1087  }
1088
1089  /* else just stay as we are... */
1090  return(topmsg);
1091}
1092
1093int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1094{
1095  int savey, i, foo, y;
1096
1097  if (curmsg<0) return(topmsg);
1098   
1099  /* If we're off the top of the screen then center */
1100  if (curmsg<topmsg) {
1101    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1102  }
1103
1104  /* If curmsg is so far past topmsg that there are more messages than
1105     lines, skip the line counting that follows because we're
1106     certainly off screen.  */
1107  savey=curmsg-topmsg;
1108  if (savey <= recwinlines) {
1109    /* Find number of lines from top to bottom of curmsg (store in savey) */
1110    savey = 0;
1111    for (i=topmsg; i<=curmsg; i++) {
1112      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1113    }
1114  }
1115
1116  /* If we're off the bottom of the screen, set the topmsg to curmsg
1117   * and scroll upwards */
1118  if (savey > recwinlines) {
1119    topmsg=curmsg;
1120    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
1121    direction=OWL_DIRECTION_UPWARDS;
1122  }
1123 
1124  /* If our bottom line is less than 1/4 down the screen then scroll up */
1125  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1126    if (savey < (recwinlines / 4)) {
1127      y=0;
1128      for (i=curmsg; i>=0; i--) {
1129        foo=owl_message_get_numlines(owl_view_get_element(v, i));
1130        /* will we run the curmsg off the screen? */
1131        if ((foo+y) >= recwinlines) {
1132          i++;
1133          if (i>curmsg) i=curmsg;
1134          break;
1135        }
1136        /* have saved 1/2 the screen space? */
1137        y+=foo;
1138        if (y > (recwinlines / 2)) break;
1139      }
1140      if (i<0) i=0;
1141      return(i);
1142    }
1143  }
1144
1145  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1146    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1147    if (savey > ((recwinlines * 3)/4)) {
1148      y=0;
1149      /* count lines from the top until we can save 1/2 the screen size */
1150      for (i=topmsg; i<curmsg; i++) {
1151        y+=owl_message_get_numlines(owl_view_get_element(v, i));
1152        if (y > (recwinlines / 2)) break;
1153      }
1154      if (i==curmsg) {
1155        i--;
1156      }
1157      return(i+1);
1158    }
1159  }
1160
1161  return(topmsg);
1162}
1163
1164void owl_function_resize(void)
1165{
1166  owl_global_set_resize_pending(&g);
1167}
1168
1169void owl_function_debugmsg(const char *fmt, ...)
1170{
1171  FILE *file;
1172  time_t now;
1173  va_list ap;
1174  va_start(ap, fmt);
1175
1176  if (!owl_global_is_debug_fast(&g))
1177    return;
1178
1179  file = owl_global_get_debug_file_handle(&g);
1180  if (!file) /* XXX should report this */
1181    return;
1182
1183  now = time(NULL);
1184
1185  fprintf(file, "[%d -  %.24s - %lds]: ",
1186          (int) getpid(), ctime(&now), now - owl_global_get_starttime(&g));
1187  vfprintf(file, fmt, ap);
1188  putc('\n', file);
1189  fflush(file);
1190
1191  va_end(ap);
1192}
1193
1194void owl_function_beep(void)
1195{
1196  if (owl_global_is_bell(&g)) {
1197    beep();
1198  }
1199}
1200
1201int owl_function_subscribe(const char *class, const char *inst, const char *recip)
1202{
1203  int ret;
1204
1205  ret=owl_zephyr_sub(class, inst, recip);
1206  if (ret) {
1207    owl_function_error("Error subscribing.");
1208  } else {
1209    owl_function_makemsg("Subscribed.");
1210  }
1211  return(ret);
1212}
1213
1214void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
1215{
1216  int ret;
1217
1218  ret=owl_zephyr_unsub(class, inst, recip);
1219  if (ret) {
1220    owl_function_error("Error subscribing.");
1221  } else {
1222    owl_function_makemsg("Unsubscribed.");
1223  }
1224}
1225
1226static void _dirty_everything(owl_window *w) {
1227  if (!owl_window_is_realized(w))
1228    return;
1229  owl_window_dirty(w);
1230  owl_window_children_foreach(w, (GFunc)_dirty_everything, NULL);
1231}
1232
1233void owl_function_full_redisplay(void)
1234{
1235  _dirty_everything(owl_window_get_screen());
1236}
1237
1238void owl_function_popless_text(const char *text)
1239{
1240  owl_popwin *pw;
1241  owl_viewwin *v;
1242
1243  pw=owl_global_get_popwin(&g);
1244  v=owl_global_get_viewwin(&g);
1245
1246  owl_popwin_up(pw);
1247  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1248  owl_viewwin_init_text(v, owl_popwin_get_content(pw), text);
1249}
1250
1251void owl_function_popless_fmtext(const owl_fmtext *fm)
1252{
1253  owl_popwin *pw;
1254  owl_viewwin *v;
1255
1256  pw=owl_global_get_popwin(&g);
1257  v=owl_global_get_viewwin(&g);
1258
1259  owl_popwin_up(pw);
1260  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1261  owl_viewwin_init_fmtext(v, owl_popwin_get_content(pw), fm);
1262}
1263
1264void owl_function_popless_file(const char *filename)
1265{
1266  owl_fmtext fm;
1267  FILE *file;
1268  char *s = NULL;
1269
1270  file=fopen(filename, "r");
1271  if (!file) {
1272    owl_function_error("Could not open file: %s", filename);
1273    return;
1274  }
1275
1276  owl_fmtext_init_null(&fm);
1277  while (owl_getline(&s, file))
1278    owl_fmtext_append_normal(&fm, s);
1279  owl_free(s);
1280
1281  owl_function_popless_fmtext(&fm);
1282  owl_fmtext_cleanup(&fm);
1283  fclose(file);
1284}
1285
1286void owl_function_about(void)
1287{
1288  owl_function_popless_text(
1289    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1290    "barnowl is a fork of the Owl zephyr client, written and\n"
1291    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1292    "Massachusetts Institute of Technology. \n"
1293    "\n"
1294    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1295    "released in March 2002.\n"
1296    "\n"
1297    "The name 'owl' was chosen in reference to the owls in the\n"
1298    "Harry Potter novels, who are tasked with carrying messages\n"
1299    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1300    "because we feel our owls should live closer to our ponies.\n"
1301    "\n"
1302    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
1303    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1304    "Copyright 2002 Massachusetts Institute of Technology\n"
1305    "\n"
1306    "This program is free software. You can redistribute it and/or\n"
1307    "modify under the terms of the Sleepycat License. Use the \n"
1308    "':show license' command to display the full license\n"
1309  );
1310}
1311
1312void owl_function_info(void)
1313{
1314  const owl_message *m;
1315  owl_fmtext fm, attrfm;
1316  const owl_view *v;
1317#ifdef HAVE_LIBZEPHYR
1318  const ZNotice_t *n;
1319#endif
1320
1321  owl_fmtext_init_null(&fm);
1322 
1323  v=owl_global_get_current_view(&g);
1324  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1325  if (!m || owl_view_get_size(v)==0) {
1326    owl_function_error("No message selected\n");
1327    return;
1328  }
1329
1330  owl_fmtext_append_bold(&fm, "General Information:\n");
1331  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1332
1333  owl_fmtext_append_normal(&fm, "  Type      : ");
1334  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1335  owl_fmtext_append_normal(&fm, "\n");
1336
1337  if (owl_message_is_direction_in(m)) {
1338    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1339  } else if (owl_message_is_direction_out(m)) {
1340    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1341  } else if (owl_message_is_direction_none(m)) {
1342    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1343  } else {
1344    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1345  }
1346
1347  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
1348
1349  if (!owl_message_is_type_admin(m)) {
1350    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1351    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1352  }
1353
1354  if (owl_message_is_type_zephyr(m)) {
1355    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1356   
1357    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1358    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1359    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1360#ifdef HAVE_LIBZEPHYR
1361    if (owl_message_is_direction_in(m)) {
1362      char *ptr, tmpbuff[1024];
1363      int i, j, fields, len;
1364
1365      n=owl_message_get_notice(m);
1366
1367      if (!owl_message_is_pseudo(m)) {
1368        owl_fmtext_append_normal(&fm, "  Kind      : ");
1369        if (n->z_kind==UNSAFE) {
1370          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1371        } else if (n->z_kind==UNACKED) {
1372          owl_fmtext_append_normal(&fm, "UNACKED\n");
1373        } else if (n->z_kind==ACKED) {
1374          owl_fmtext_append_normal(&fm, "ACKED\n");
1375        } else if (n->z_kind==HMACK) {
1376          owl_fmtext_append_normal(&fm, "HMACK\n");
1377        } else if (n->z_kind==HMCTL) {
1378          owl_fmtext_append_normal(&fm, "HMCTL\n");
1379        } else if (n->z_kind==SERVACK) {
1380          owl_fmtext_append_normal(&fm, "SERVACK\n");
1381        } else if (n->z_kind==SERVNAK) {
1382          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1383        } else if (n->z_kind==CLIENTACK) {
1384          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1385        } else if (n->z_kind==STAT) {
1386          owl_fmtext_append_normal(&fm, "STAT\n");
1387        } else {
1388          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1389        }
1390      }
1391      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1392
1393      if (!owl_message_is_pseudo(m)) {
1394        owl_fmtext_append_normal(&fm, "\n");
1395        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1396        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1397
1398        /* FIXME make these more descriptive */
1399        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1400        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1401        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1402        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1403
1404        fields=owl_zephyr_get_num_fields(n);
1405        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1406
1407        for (i=0; i<fields; i++) {
1408          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
1409          len=strlen(ptr);
1410          if (len<30) {
1411            strncpy(tmpbuff, ptr, len);
1412            tmpbuff[len]='\0';
1413          } else {
1414            strncpy(tmpbuff, ptr, 30);
1415            tmpbuff[30]='\0';
1416            strcat(tmpbuff, "...");
1417          }
1418          owl_free(ptr);
1419
1420          for (j=0; j<strlen(tmpbuff); j++) {
1421            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1422            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1423          }
1424
1425          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
1426        }
1427        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
1428      }
1429
1430    }
1431#endif
1432  }
1433
1434  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1435  owl_message_attributes_tofmtext(m, &attrfm);
1436  owl_fmtext_append_fmtext(&fm, &attrfm);
1437 
1438  owl_function_popless_fmtext(&fm);
1439  owl_fmtext_cleanup(&fm);
1440  owl_fmtext_cleanup(&attrfm);
1441}
1442
1443/* print the current message in a popup window.
1444 * Use the 'default' style regardless of whatever
1445 * style the user may be using
1446 */
1447void owl_function_curmsg_to_popwin(void)
1448{
1449  const owl_view *v;
1450  const owl_message *m;
1451  const owl_style *s;
1452  owl_fmtext fm;
1453
1454  v=owl_global_get_current_view(&g);
1455  s=owl_global_get_style_by_name(&g, "default");
1456
1457  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1458
1459  if (!m || owl_view_get_size(v)==0) {
1460    owl_function_error("No current message");
1461    return;
1462  }
1463
1464  owl_fmtext_init_null(&fm);
1465  owl_style_get_formattext(s, &fm, m);
1466
1467  owl_function_popless_fmtext(&fm);
1468  owl_fmtext_cleanup(&fm);
1469}
1470
1471void owl_function_page_curmsg(int step)
1472{
1473  /* scroll down or up within the current message IF the message is truncated */
1474
1475  int offset, curmsg, lines;
1476  const owl_view *v;
1477  owl_message *m;
1478
1479  offset=owl_global_get_curmsg_vert_offset(&g);
1480  v=owl_global_get_current_view(&g);
1481  curmsg=owl_global_get_curmsg(&g);
1482  m=owl_view_get_element(v, curmsg);
1483  if (!m || owl_view_get_size(v)==0) return;
1484  lines=owl_message_get_numlines(m);
1485
1486  if (offset==0) {
1487    /* Bail if the curmsg isn't the last one displayed */
1488    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1489      owl_function_makemsg("The entire message is already displayed");
1490      return;
1491    }
1492   
1493    /* Bail if we're not truncated */
1494    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1495      owl_function_makemsg("The entire message is already displayed");
1496      return;
1497    }
1498  }
1499 
1500 
1501  /* don't scroll past the last line */
1502  if (step>0) {
1503    if (offset+step > lines-1) {
1504      owl_global_set_curmsg_vert_offset(&g, lines-1);
1505    } else {
1506      owl_global_set_curmsg_vert_offset(&g, offset+step);
1507    }
1508  }
1509
1510  /* would we be before the beginning of the message? */
1511  if (step<0) {
1512    if (offset+step<0) {
1513      owl_global_set_curmsg_vert_offset(&g, 0);
1514    } else {
1515      owl_global_set_curmsg_vert_offset(&g, offset+step);
1516    }
1517  }
1518 
1519  /* redisplay */
1520  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1521}
1522
1523void owl_function_resize_typwin(int newsize)
1524{
1525  owl_global_set_typwin_lines(&g, newsize);
1526  owl_mainpanel_layout_contents(&g.mainpanel);
1527}
1528
1529void owl_function_mainwin_pagedown(void)
1530{
1531  int i;
1532
1533  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1534  if (i<0) return;
1535  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1536      && (owl_global_get_curmsg(&g) < i)
1537      && (i>0)) {
1538    i--;
1539  }
1540  owl_global_set_curmsg(&g, i);
1541  owl_function_nextmsg();
1542}
1543
1544void owl_function_mainwin_pageup(void)
1545{
1546  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1547  owl_function_prevmsg();
1548}
1549
1550void owl_function_getsubs(void)
1551{
1552  char *buff;
1553
1554  buff=owl_zephyr_getsubs();
1555
1556  if (buff) {
1557    owl_function_popless_text(buff);
1558  } else {
1559    owl_function_popless_text("Error getting subscriptions");
1560  }
1561           
1562  owl_free(buff);
1563}
1564
1565void owl_function_printallvars(void)
1566{
1567  const char *name;
1568  char var[LINE];
1569  owl_list varnames;
1570  int i, numvarnames;
1571  GString *str   = g_string_new("");
1572
1573  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1574  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1575  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1576  numvarnames = owl_list_get_size(&varnames);
1577  for (i=0; i<numvarnames; i++) {
1578    name = owl_list_get_element(&varnames, i);
1579    if (name && name[0]!='_') {
1580      g_string_append_printf(str, "\n%-20s = ", name);
1581      owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE);
1582      g_string_append(str, var);
1583    }
1584  }
1585  g_string_append(str, "\n");
1586  owl_variable_dict_namelist_cleanup(&varnames);
1587
1588  owl_function_popless_text(str->str);
1589  g_string_free(str, TRUE);
1590}
1591
1592void owl_function_show_variables(void)
1593{
1594  owl_list varnames;
1595  owl_fmtext fm; 
1596  int i, numvarnames;
1597  const char *varname;
1598
1599  owl_fmtext_init_null(&fm);
1600  owl_fmtext_append_bold(&fm, 
1601      "Variables: (use 'show variable <name>' for details)\n");
1602  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1603  numvarnames = owl_list_get_size(&varnames);
1604  for (i=0; i<numvarnames; i++) {
1605    varname = owl_list_get_element(&varnames, i);
1606    if (varname && varname[0]!='_') {
1607      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1608    }
1609  }
1610  owl_variable_dict_namelist_cleanup(&varnames);
1611  owl_function_popless_fmtext(&fm);
1612  owl_fmtext_cleanup(&fm);
1613}
1614
1615void owl_function_show_variable(const char *name)
1616{
1617  owl_fmtext fm; 
1618
1619  owl_fmtext_init_null(&fm);
1620  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1621  owl_function_popless_fmtext(&fm);
1622  owl_fmtext_cleanup(&fm);
1623}
1624
1625/* note: this applies to global message list, not to view.
1626 * If flag is 1, deletes.  If flag is 0, undeletes. */
1627void owl_function_delete_by_id(int id, int flag)
1628{
1629  const owl_messagelist *ml;
1630  owl_message *m;
1631  ml = owl_global_get_msglist(&g);
1632  m = owl_messagelist_get_by_id(ml, id);
1633  if (m) {
1634    if (flag == 1) {
1635      owl_message_mark_delete(m);
1636    } else if (flag == 0) {
1637      owl_message_unmark_delete(m);
1638    }
1639    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1640  } else {
1641    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1642  }
1643}
1644
1645void owl_function_delete_automsgs(void)
1646{
1647  /* mark for deletion all messages in the current view that match the
1648   * 'trash' filter */
1649
1650  int i, j, count;
1651  owl_message *m;
1652  const owl_view *v;
1653  const owl_filter *f;
1654
1655  /* get the trash filter */
1656  f=owl_global_get_filter(&g, "trash");
1657  if (!f) {
1658    owl_function_error("No trash filter defined");
1659    return;
1660  }
1661
1662  v=owl_global_get_current_view(&g);
1663
1664  count=0;
1665  j=owl_view_get_size(v);
1666  for (i=0; i<j; i++) {
1667    m=owl_view_get_element(v, i);
1668    if (owl_filter_message_match(f, m)) {
1669      count++;
1670      owl_message_mark_delete(m);
1671    }
1672  }
1673  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1674  owl_function_makemsg("%i messages marked for deletion", count);
1675}
1676
1677void owl_function_status(void)
1678{
1679  char buff[MAXPATHLEN+1];
1680  time_t start;
1681  int up, days, hours, minutes;
1682  owl_fmtext fm;
1683
1684  owl_fmtext_init_null(&fm);
1685
1686  start=owl_global_get_starttime(&g);
1687
1688  owl_fmtext_append_normal(&fm, "General Information:\n");
1689
1690  owl_fmtext_append_normal(&fm, "  Version: ");
1691  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1692  owl_fmtext_append_normal(&fm, "\n");
1693
1694  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1695  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1696  owl_fmtext_append_normal(&fm, "\n");
1697
1698  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1699  if(getcwd(buff, MAXPATHLEN) == NULL) {
1700    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1701  } else {
1702    owl_fmtext_append_normal(&fm, buff);
1703  }
1704  owl_fmtext_append_normal(&fm, "\n");
1705
1706  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
1707
1708  up=owl_global_get_runtime(&g);
1709  days=up/86400;
1710  up-=days*86400;
1711  hours=up/3600;
1712  up-=hours*3600;
1713  minutes=up/60;
1714  up-=minutes*60;
1715  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1716
1717  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1718  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1719  if (owl_global_is_havezephyr(&g)) {
1720    owl_fmtext_append_normal(&fm, "yes\n");
1721  } else {
1722    owl_fmtext_append_normal(&fm, "no\n");
1723  }
1724  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1725  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1726
1727
1728  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1729  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1730#if OWL_STDERR_REDIR
1731  owl_fmtext_append_normal(&fm, "yes\n");
1732#else
1733  owl_fmtext_append_normal(&fm, "no\n");
1734#endif
1735 
1736
1737  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1738  owl_fmtext_append_normal(&fm, "  Logged in: ");
1739  if (owl_global_is_aimloggedin(&g)) {
1740    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1741    owl_fmtext_append_normal(&fm, "\n");
1742  } else {
1743    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1744  }
1745
1746  owl_fmtext_append_normal(&fm, "  Processing events: ");
1747  if (owl_global_is_doaimevents(&g)) {
1748    owl_fmtext_append_normal(&fm, "yes\n");
1749  } else {
1750    owl_fmtext_append_normal(&fm, "no\n");
1751  }
1752
1753  owl_function_popless_fmtext(&fm);
1754  owl_fmtext_cleanup(&fm);
1755}
1756
1757void owl_function_show_term(void)
1758{
1759  owl_fmtext fm;
1760
1761  owl_fmtext_init_null(&fm);
1762  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1763          owl_global_get_lines(&g),
1764          owl_global_get_cols(&g));
1765
1766  if (owl_global_get_hascolors(&g)) {
1767    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1768    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1769    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1770  } else {
1771    owl_fmtext_append_normal(&fm, "Color: No\n");
1772  }
1773
1774  owl_function_popless_fmtext(&fm);
1775  owl_fmtext_cleanup(&fm);
1776}
1777
1778/* if type = 0 then normal reply.
1779 * if type = 1 then it's a reply to sender
1780 * if enter = 0 then allow the command to be edited
1781 * if enter = 1 then don't wait for editing
1782 */
1783void owl_function_reply(int type, int enter)
1784{
1785  char *buff=NULL;
1786  const owl_message *m;
1787  const owl_filter *f;
1788 
1789  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1790    owl_function_error("No message selected");
1791  } else {
1792    char *cmd;
1793   
1794    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1795    if (!m) {
1796      owl_function_error("No message selected");
1797      return;
1798    }
1799
1800    /* first check if we catch the reply-lockout filter */
1801    f=owl_global_get_filter(&g, "reply-lockout");
1802    if (f) {
1803      if (owl_filter_message_match(f, m)) {
1804        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1805        return;
1806      }
1807    }
1808
1809    /* then check if it's a question and just bring up the command prompt */
1810    if (owl_message_is_question(m)) {
1811      owl_function_start_command("");
1812      return;
1813    }
1814
1815    if((type == 0 &&
1816        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1817       (type == 1 &&
1818        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1819      buff = cmd;
1820    }
1821
1822    if(!buff) {
1823        owl_function_error("I don't know how to reply to that message.");
1824        return;
1825    }
1826
1827    if (enter) {
1828      owl_history *hist = owl_global_get_cmd_history(&g);
1829      owl_history_store(hist, buff);
1830      owl_history_reset(hist);
1831      owl_function_command_norv(buff);
1832    } else {
1833      owl_function_start_command(buff);
1834    }
1835    owl_free(buff);
1836  }
1837}
1838
1839void owl_function_zlocate(int argc, const char *const *argv, int auth)
1840{
1841  owl_fmtext fm;
1842  char *ptr;
1843  char *result;
1844  int i;
1845
1846  owl_fmtext_init_null(&fm);
1847
1848  for (i=0; i<argc; i++) {
1849    ptr = long_zuser(argv[i]);
1850    result = owl_zephyr_zlocate(ptr, auth);
1851    owl_fmtext_append_normal(&fm, result);
1852    owl_free(result);
1853    owl_free(ptr);
1854  }
1855
1856  owl_function_popless_fmtext(&fm);
1857  owl_fmtext_cleanup(&fm);
1858}
1859
1860void owl_callback_command(owl_editwin *e)
1861{
1862  char *rv;
1863  const char *line = owl_editwin_get_text(e);
1864
1865  rv = owl_function_command(line);
1866   if (rv) {
1867    owl_function_makemsg("%s", rv);
1868    owl_free(rv);
1869  }
1870}
1871
1872void owl_function_start_command(const char *line)
1873{
1874  owl_editwin *tw;
1875
1876  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1877
1878  owl_editwin_set_locktext(tw, "command: ");
1879
1880  owl_editwin_insert_string(tw, line);
1881
1882  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline", owl_global_get_typwin_window(&g));
1883  owl_editwin_set_callback(tw, owl_callback_command);
1884}
1885
1886owl_editwin *owl_function_start_question(const char *line)
1887{
1888  owl_editwin *tw;
1889
1890  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1891
1892  owl_editwin_set_locktext(tw, line);
1893
1894  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse", owl_global_get_typwin_window(&g));
1895  return tw;
1896}
1897
1898owl_editwin *owl_function_start_password(const char *line)
1899{
1900  owl_editwin *tw;
1901
1902  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1903
1904  owl_editwin_set_echochar(tw, '*');
1905
1906  owl_editwin_set_locktext(tw, line);
1907
1908  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse", owl_global_get_typwin_window(&g));
1909  return tw;
1910}
1911
1912char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
1913{
1914  /* if type == 1 display in a popup
1915   * if type == 2 display an admin messages
1916   * if type == 0 return output
1917   * else display in a popup
1918   */
1919  const char *redirect = " 2>&1 < /dev/null";
1920  char *newbuff;
1921  char *out;
1922  FILE *p;
1923
1924#if OWL_STDERR_REDIR
1925  redirect = " < /dev/null";
1926#endif
1927
1928  if (argc<2) {
1929    owl_function_error("Wrong number of arguments to the exec command");
1930    return NULL;
1931  }
1932
1933  buff = skiptokens(buff, 1);
1934  newbuff = owl_sprintf("%s%s", buff, redirect);
1935
1936  if (type == OWL_OUTPUT_POPUP) {
1937    owl_popexec_new(newbuff);
1938  } else {
1939    p = popen(newbuff, "r");
1940    out = owl_slurp(p);
1941    pclose(p);
1942   
1943    if (type == OWL_OUTPUT_RETURN) {
1944      owl_free(newbuff);
1945      return out;
1946    } else if (type == OWL_OUTPUT_ADMINMSG) {
1947      owl_function_adminmsg(buff, out);
1948    }
1949    owl_free(out);
1950  }
1951  owl_free(newbuff);
1952  return NULL;
1953}
1954
1955char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
1956{
1957  /* if type == 1 display in a popup
1958   * if type == 2 display an admin messages
1959   * if type == 0 return output
1960   * else display in a popup
1961   */
1962  char *perlout;
1963
1964  if (argc<2) {
1965    owl_function_error("Wrong number of arguments to perl command");
1966    return NULL;
1967  }
1968
1969  /* consume first token (argv[0]) */
1970  buff = skiptokens(buff, 1);
1971
1972  perlout = owl_perlconfig_execute(buff);
1973  if (perlout) { 
1974    if (type == OWL_OUTPUT_POPUP) {
1975      owl_function_popless_text(perlout);
1976    } else if (type == OWL_OUTPUT_ADMINMSG) {
1977      owl_function_adminmsg(buff, perlout);
1978    } else if (type == OWL_OUTPUT_RETURN) {
1979      return perlout;
1980    }
1981    owl_free(perlout);
1982  }
1983  return NULL;
1984}
1985
1986/* Change the filter associated with the current view.
1987 * This also figures out which message in the new filter
1988 * should have the pointer.
1989 */
1990void owl_function_change_currentview_filter(const char *filtname)
1991{
1992  owl_view *v;
1993  owl_filter *f;
1994  int curid=-1, newpos, curmsg;
1995  const owl_message *curm=NULL;
1996
1997  v=owl_global_get_current_view(&g);
1998
1999  curmsg=owl_global_get_curmsg(&g);
2000  if (curmsg==-1) {
2001    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2002  } else {
2003    curm=owl_view_get_element(v, curmsg);
2004    if (curm) {
2005      curid=owl_message_get_id(curm);
2006      owl_view_save_curmsgid(v, curid);
2007    }
2008  }
2009
2010  f=owl_global_get_filter(&g, filtname);
2011  if (!f) {
2012    owl_function_error("Unknown filter %s", filtname);
2013    return;
2014  }
2015
2016  owl_view_new_filter(v, f);
2017
2018  /* Figure out what to set the current message to.
2019   * - If the view we're leaving has messages in it, go to the closest message
2020   *   to the last message pointed to in that view.
2021   * - If the view we're leaving is empty, try to restore the position
2022   *   from the last time we were in the new view.  */
2023  if (curm) {
2024    newpos = owl_view_get_nearest_to_msgid(v, curid);
2025  } else {
2026    newpos = owl_view_get_nearest_to_saved(v);
2027  }
2028
2029  owl_global_set_curmsg(&g, newpos);
2030  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2031  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2032  owl_global_set_direction_downwards(&g);
2033}
2034
2035/* Create a new filter, or replace an existing one
2036 * with a new definition.
2037 */
2038void owl_function_create_filter(int argc, const char *const *argv)
2039{
2040  owl_filter *f;
2041  const owl_view *v;
2042  int inuse = 0;
2043
2044  if (argc < 2) {
2045    owl_function_error("Wrong number of arguments to filter command");
2046    return;
2047  }
2048
2049  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2050
2051  v=owl_global_get_current_view(&g);
2052
2053  /* don't touch the all filter */
2054  if (!strcmp(argv[1], "all")) {
2055    owl_function_error("You may not change the 'all' filter.");
2056    return;
2057  }
2058
2059  /* deal with the case of trying change the filter color */
2060  if (argc==4 && !strcmp(argv[2], "-c")) {
2061    f=owl_global_get_filter(&g, argv[1]);
2062    if (!f) {
2063      owl_function_error("The filter '%s' does not exist.", argv[1]);
2064      return;
2065    }
2066    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2067      owl_function_error("The color '%s' is not available.", argv[3]);
2068      return;
2069    }
2070    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2071    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2072    return;
2073  }
2074  if (argc==4 && !strcmp(argv[2], "-b")) {
2075    f=owl_global_get_filter(&g, argv[1]);
2076    if (!f) {
2077      owl_function_error("The filter '%s' does not exist.", argv[1]);
2078      return;
2079    }
2080    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2081      owl_function_error("The color '%s' is not available.", argv[3]);
2082      return;
2083    }
2084    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2085    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2086    return;
2087  }
2088
2089  /* create the filter and check for errors */
2090  f = owl_filter_new(argv[1], argc-2, argv+2);
2091  if (f == NULL) {
2092    owl_function_error("Invalid filter");
2093    return;
2094  }
2095
2096  /* if the named filter is in use by the current view, remember it */
2097  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2098    inuse=1;
2099  }
2100
2101  /* if the named filter already exists, nuke it */
2102  if (owl_global_get_filter(&g, argv[1])) {
2103    owl_global_remove_filter(&g, argv[1]);
2104  }
2105
2106  /* add the filter */
2107  owl_global_add_filter(&g, f);
2108
2109  /* if it was in use by the current view then update */
2110  if (inuse) {
2111    owl_function_change_currentview_filter(argv[1]);
2112  }
2113  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2114}
2115
2116/* If 'filtername' does not start with 'not-' create a filter named
2117 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2118 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2119 * 'filtername' begins with 'not-' and a filter 'filtername' already
2120 * exists, then do nothing.  If the filter 'filtername' does not
2121 * exist, create it and define it as 'not filter <filtername>'
2122 *
2123 * Returns the name of the negated filter, which the caller must free.
2124 */
2125char *owl_function_create_negative_filter(const char *filtername)
2126{
2127  char *newname;
2128  const owl_filter *tmpfilt;
2129  const char *argv[5];
2130
2131  owl_function_debugmsg("owl_function_create_negative_filter");
2132 
2133  if (!strncmp(filtername, "not-", 4)) {
2134    newname=owl_strdup(filtername+4);
2135  } else {
2136    newname=owl_sprintf("not-%s", filtername);
2137  }
2138
2139  tmpfilt=owl_global_get_filter(&g, newname);
2140  if (!tmpfilt) {
2141    argv[0]="filter"; /* anything is fine here */
2142    argv[1]=newname;
2143    argv[2]="not";
2144    argv[3]="filter";
2145    argv[4]=filtername;
2146    owl_function_create_filter(5, argv);
2147  }
2148
2149  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2150  return(newname);
2151}
2152
2153void owl_function_show_filters(void)
2154{
2155  const owl_filter *f;
2156  GList *fl;
2157  owl_fmtext fm;
2158
2159  owl_fmtext_init_null(&fm);
2160
2161  owl_fmtext_append_bold(&fm, "Filters:\n");
2162
2163  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2164    f = fl->data;
2165    owl_fmtext_append_normal(&fm, "   ");
2166    if (owl_global_get_hascolors(&g)) {
2167      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2168    } else {
2169      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2170    }
2171    owl_fmtext_append_normal(&fm, "\n");
2172  }
2173  owl_function_popless_fmtext(&fm);
2174  owl_fmtext_cleanup(&fm);
2175}
2176
2177void owl_function_show_filter(const char *name)
2178{
2179  const owl_filter *f;
2180  char *buff, *tmp;
2181
2182  f=owl_global_get_filter(&g, name);
2183  if (!f) {
2184    owl_function_error("There is no filter named %s", name);
2185    return;
2186  }
2187  tmp = owl_filter_print(f);
2188  buff = owl_sprintf("%s: %s", owl_filter_get_name(f), tmp);
2189  owl_function_popless_text(buff);
2190  owl_free(buff);
2191  owl_free(tmp);
2192}
2193
2194void owl_function_show_zpunts(void)
2195{
2196  const owl_filter *f;
2197  const owl_list *fl;
2198  char buff[5000];
2199  char *tmp;
2200  owl_fmtext fm;
2201  int i, j;
2202
2203  owl_fmtext_init_null(&fm);
2204
2205  fl=owl_global_get_puntlist(&g);
2206  j=owl_list_get_size(fl);
2207  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2208
2209  for (i=0; i<j; i++) {
2210    f=owl_list_get_element(fl, i);
2211    snprintf(buff, sizeof(buff), "[% 2d] ", i+1);
2212    owl_fmtext_append_normal(&fm, buff);
2213    tmp = owl_filter_print(f);
2214    owl_fmtext_append_normal(&fm, tmp);
2215    owl_free(tmp);
2216  }
2217  owl_function_popless_fmtext(&fm);
2218  owl_fmtext_cleanup(&fm);
2219}
2220
2221/* Create a filter for a class, instance if one doesn't exist.  If
2222 * instance is NULL then catch all messgaes in the class.  Returns the
2223 * name of the filter, which the caller must free.
2224 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2225 */
2226char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2227{
2228  owl_filter *f;
2229  char *argbuff, *filtname;
2230  char *tmpclass, *tmpinstance = NULL;
2231  char *class, *instance = NULL;
2232
2233  if (related) {
2234    class = owl_util_baseclass(c);
2235    if (i) {
2236      instance = owl_util_baseclass(i);
2237    }
2238  } else {
2239    class = owl_strdup(c);
2240    if (i) {
2241      instance = owl_strdup(i);
2242    }
2243  }
2244
2245  /* name for the filter */
2246  if (!instance) {
2247    filtname = owl_sprintf("%sclass-%s", related ? "related-" : "", class);
2248  } else {
2249    filtname = owl_sprintf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2250  }
2251  /* downcase it */
2252  {
2253    char *temp = g_utf8_strdown(filtname, -1);
2254    if (temp) {
2255      owl_free(filtname);
2256      filtname = temp;
2257    }
2258  }
2259  /* turn spaces, single quotes, and double quotes into dots */
2260  owl_text_tr(filtname, ' ', '.');
2261  owl_text_tr(filtname, '\'', '.');
2262  owl_text_tr(filtname, '"', '.');
2263 
2264  /* if it already exists then go with it.  This lets users override */
2265  if (owl_global_get_filter(&g, filtname)) {
2266    goto done;
2267  }
2268
2269  /* create the new filter */
2270  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2271  owl_text_tr(tmpclass, ' ', '.');
2272  owl_text_tr(tmpclass, '\'', '.');
2273  owl_text_tr(tmpclass, '"', '.');
2274  if (instance) {
2275    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2276    owl_text_tr(tmpinstance, ' ', '.');
2277    owl_text_tr(tmpinstance, '\'', '.');
2278    owl_text_tr(tmpinstance, '"', '.');
2279  }
2280
2281  argbuff = owl_sprintf(related ? "class ^(un)*%s(\\.d)*$" : "class ^%s$", tmpclass);
2282  if (tmpinstance) {
2283    char *tmp = argbuff;
2284    argbuff = owl_sprintf(related ? "%s and ( instance ^(un)*%s(\\.d)*$ )" : "%s and instance ^%s$", tmp, tmpinstance);
2285    owl_free(tmp);
2286  }
2287  owl_free(tmpclass);
2288  if (tmpinstance) owl_free(tmpinstance);
2289
2290  f = owl_filter_new_fromstring(filtname, argbuff);
2291
2292  /* add it to the global list */
2293  owl_global_add_filter(&g, f);
2294
2295  owl_free(argbuff);
2296done:
2297  owl_free(class);
2298  if (instance) {
2299    owl_free(instance);
2300  }
2301  return(filtname);
2302}
2303
2304/* Create a filter for personal zephyrs to or from the specified
2305 * zephyr user.  Includes login/logout notifications for the user.
2306 * The name of the filter will be 'user-<user>'.  If a filter already
2307 * exists with this name, no new filter will be created.  This allows
2308 * the configuration to override this function.  Returns the name of
2309 * the filter, which the caller must free.
2310 */
2311char *owl_function_zuserfilt(const char *user)
2312{
2313  owl_filter *f;
2314  char *argbuff, *longuser, *esclonguser, *shortuser, *filtname;
2315
2316  /* stick the local realm on if it's not there */
2317  longuser=long_zuser(user);
2318  shortuser=short_zuser(user);
2319
2320  /* name for the filter */
2321  filtname=owl_sprintf("user-%s", shortuser);
2322
2323  /* if it already exists then go with it.  This lets users override */
2324  if (owl_global_get_filter(&g, filtname)) {
2325    return filtname;
2326  }
2327
2328  /* create the new-internal filter */
2329  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2330
2331  argbuff=owl_sprintf("( type ^zephyr$ and filter personal and "
2332      "( ( direction ^in$ and sender ^%1$s$ ) or ( direction ^out$ and "
2333      "recipient ^%1$s$ ) ) ) or ( ( class ^login$ ) and ( sender ^%1$s$ ) )",
2334      esclonguser);
2335
2336  f = owl_filter_new_fromstring(filtname, argbuff);
2337
2338  /* add it to the global list */
2339  owl_global_add_filter(&g, f);
2340
2341  /* free stuff */
2342  owl_free(argbuff);
2343  owl_free(longuser);
2344  owl_free(esclonguser);
2345  owl_free(shortuser);
2346
2347  return(filtname);
2348}
2349
2350/* Create a filter for AIM IM messages to or from the specified
2351 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2352 * filter already exists with this name, no new filter will be
2353 * created.  This allows the configuration to override this function.
2354 * Returns the name of the filter, which the caller must free.
2355 */
2356char *owl_function_aimuserfilt(const char *user)
2357{
2358  owl_filter *f;
2359  char *argbuff, *filtname;
2360  char *escuser;
2361
2362  /* name for the filter */
2363  filtname=owl_sprintf("aimuser-%s", user);
2364
2365  /* if it already exists then go with it.  This lets users override */
2366  if (owl_global_get_filter(&g, filtname)) {
2367    return(owl_strdup(filtname));
2368  }
2369
2370  /* create the new-internal filter */
2371  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2372
2373  argbuff = owl_sprintf(
2374      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2375      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2376      escuser, owl_global_get_aim_screenname_for_filters(&g));
2377
2378  f = owl_filter_new_fromstring(filtname, argbuff);
2379
2380  /* add it to the global list */
2381  owl_global_add_filter(&g, f);
2382
2383  /* free stuff */
2384  owl_free(argbuff);
2385  owl_free(escuser);
2386
2387  return(filtname);
2388}
2389
2390char *owl_function_typefilt(const char *type)
2391{
2392  owl_filter *f;
2393  char *argbuff, *filtname, *esctype;
2394
2395  /* name for the filter */
2396  filtname=owl_sprintf("type-%s", type);
2397
2398  /* if it already exists then go with it.  This lets users override */
2399  if (owl_global_get_filter(&g, filtname)) {
2400    return filtname;
2401  }
2402
2403  /* create the new-internal filter */
2404  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2405
2406  argbuff = owl_sprintf("type ^%s$", esctype);
2407
2408  f = owl_filter_new_fromstring(filtname, argbuff);
2409
2410  /* add it to the global list */
2411  owl_global_add_filter(&g, f);
2412
2413  /* free stuff */
2414  owl_free(argbuff);
2415  owl_free(esctype);
2416
2417  return filtname;
2418}
2419
2420/* If flag is 1, marks for deletion.  If flag is 0,
2421 * unmarks for deletion. */
2422void owl_function_delete_curview_msgs(int flag)
2423{
2424  const owl_view *v;
2425  int i, j;
2426
2427  v=owl_global_get_current_view(&g);
2428  j=owl_view_get_size(v);
2429  for (i=0; i<j; i++) {
2430    if (flag == 1) {
2431      owl_message_mark_delete(owl_view_get_element(v, i));
2432    } else if (flag == 0) {
2433      owl_message_unmark_delete(owl_view_get_element(v, i));
2434    }
2435  }
2436
2437  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2438
2439  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2440}
2441
2442static char *owl_function_smartfilter_cc(const owl_message *m) {
2443  const char *ccs;
2444  char *filtname;
2445  char *text;
2446  owl_filter *f;
2447
2448  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2449
2450  filtname = owl_sprintf("conversation-%s", ccs);
2451  owl_text_tr(filtname, ' ', '-');
2452
2453  if (owl_global_get_filter(&g, filtname)) {
2454    return filtname;
2455  }
2456
2457  text = owl_sprintf("type ^zephyr$ and filter personal and "
2458                     "zephyr_ccs ^%s%s%s$",
2459                     owl_getquoting(ccs), ccs, owl_getquoting(ccs));
2460
2461  f = owl_filter_new_fromstring(filtname, text);
2462
2463  owl_global_add_filter(&g, f);
2464
2465  owl_free(text);
2466
2467  return filtname;
2468}
2469
2470/* Create a filter based on the current message.  Returns the name of
2471 * a filter or null.  The caller must free this name.
2472 *
2473 * if the curmsg is a personal zephyr return a filter name
2474 *    to the zephyr conversation with that user.
2475 * If the curmsg is a zephyr class message, instance foo, recip *,
2476 *    return a filter name to the class, inst.
2477 * If the curmsg is a zephyr class message and type==0 then
2478 *    return a filter name for just the class.
2479 * If the curmsg is a zephyr class message and type==1 then
2480 *    return a filter name for the class and instance.
2481 * If the curmsg is a personal AIM message returna  filter
2482 *    name to the AIM conversation with that user
2483 */
2484char *owl_function_smartfilter(int type, int invert_related)
2485{
2486  const owl_view *v;
2487  const owl_message *m;
2488  char *zperson, *filtname=NULL;
2489  const char *argv[2];
2490  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2491
2492  v=owl_global_get_current_view(&g);
2493  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2494
2495  if (!m || owl_view_get_size(v)==0) {
2496    owl_function_error("No message selected\n");
2497    return(NULL);
2498  }
2499
2500  /* very simple handling of admin messages for now */
2501  if (owl_message_is_type_admin(m)) {
2502    return(owl_function_typefilt("admin"));
2503  }
2504
2505  /* very simple handling of loopback messages for now */
2506  if (owl_message_is_type_loopback(m)) {
2507    return(owl_function_typefilt("loopback"));
2508  }
2509
2510  /* aim messages */
2511  if (owl_message_is_type_aim(m)) {
2512    if (owl_message_is_direction_in(m)) {
2513      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2514    } else if (owl_message_is_direction_out(m)) {
2515      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2516    }
2517    return(filtname);
2518  }
2519
2520  /* narrow personal and login messages to the sender or recip as appropriate */
2521  if (owl_message_is_type_zephyr(m)) {
2522    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2523      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2524        return owl_function_smartfilter_cc(m);
2525      }
2526
2527      if (owl_message_is_direction_in(m)) {
2528        zperson=short_zuser(owl_message_get_sender(m));
2529      } else {
2530        zperson=short_zuser(owl_message_get_recipient(m));
2531      }
2532      filtname=owl_function_zuserfilt(zperson);
2533      owl_free(zperson);
2534      return(filtname);
2535    }
2536
2537    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2538    if (!strcasecmp(owl_message_get_class(m), "message")) {
2539      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2540      return(filtname);
2541    }
2542
2543    /* otherwise narrow to the class */
2544    if (type==0) {
2545      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2546    } else if (type==1) {
2547      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2548    }
2549    return(filtname);
2550  }
2551
2552  /* pass it off to perl */
2553  argv[0] = type ? "1" : "0";
2554  argv[1] = related ? "1" : "0";
2555  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2556}
2557
2558void owl_function_smartzpunt(int type)
2559{
2560  /* Starts a zpunt command based on the current class,instance pair.
2561   * If type=0, uses just class.  If type=1, uses instance as well. */
2562  const owl_view *v;
2563  const owl_message *m;
2564  const char *cmdprefix, *mclass, *minst;
2565  char *cmd;
2566 
2567  v=owl_global_get_current_view(&g);
2568  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2569
2570  if (!m || owl_view_get_size(v)==0) {
2571    owl_function_error("No message selected\n");
2572    return;
2573  }
2574
2575  /* for now we skip admin messages. */
2576  if (owl_message_is_type_admin(m)
2577      || owl_message_is_loginout(m)
2578      || !owl_message_is_type_zephyr(m)) {
2579    owl_function_error("smartzpunt doesn't support this message type.");
2580    return;
2581  }
2582
2583  mclass = owl_message_get_class(m);
2584  minst = owl_message_get_instance(m);
2585  if (!mclass || !*mclass || *mclass==' '
2586      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2587      || (type && (!minst || !*minst|| *minst==' '))) {
2588    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2589                         mclass, minst);
2590  } else {
2591    cmdprefix = "start-command zpunt ";
2592    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10);
2593    strcpy(cmd, cmdprefix);
2594    strcat(cmd, owl_getquoting(mclass));
2595    strcat(cmd, mclass);
2596    strcat(cmd, owl_getquoting(mclass));
2597    if (type) {
2598      strcat(cmd, " ");
2599      strcat(cmd, owl_getquoting(minst));
2600      strcat(cmd, minst);
2601      strcat(cmd, owl_getquoting(minst));
2602    } else {
2603      strcat(cmd, " *");
2604    }
2605    owl_function_command(cmd);
2606    owl_free(cmd);
2607  }
2608}
2609
2610/* Set the color of the current view's filter to
2611 * be 'color'
2612 */
2613void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2614{
2615  const char *name;
2616
2617  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2618  owl_function_color_filter(name, fgcolor, bgcolor);
2619}
2620
2621/* Set the color of the filter 'filter' to be 'color'.  If the color
2622 * name does not exist, return -1, if the filter does not exist or is
2623 * the "all" filter, return -2.  Return 0 on success
2624 */
2625int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2626{
2627  owl_filter *f;
2628
2629  f=owl_global_get_filter(&g, filtname);
2630  if (!f) {
2631    owl_function_error("Unknown filter");
2632    return(-2);
2633  }
2634
2635  /* don't touch the all filter */
2636  if (!strcmp(filtname, "all")) {
2637    owl_function_error("You may not change the 'all' filter.");
2638    return(-2);
2639  }
2640
2641  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2642    owl_function_error("No color named '%s' avilable.", fgcolor);
2643    return(-1);
2644  }
2645
2646
2647  if (bgcolor != NULL) {
2648    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2649      owl_function_error("No color named '%s' avilable.", bgcolor);
2650      return(-1);
2651    }
2652    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2653  }
2654  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2655 
2656  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2657  return(0);
2658}
2659
2660void owl_function_show_colors(void)
2661{
2662  owl_fmtext fm;
2663  int i; 
2664 
2665  owl_fmtext_init_null(&fm);
2666  owl_fmtext_append_normal(&fm, "default: ");
2667  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2668
2669  owl_fmtext_append_normal(&fm,"red:      ");
2670  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2671
2672  owl_fmtext_append_normal(&fm,"green:    ");
2673  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2674
2675  owl_fmtext_append_normal(&fm,"yellow:   ");
2676  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2677
2678  owl_fmtext_append_normal(&fm,"blue:     ");
2679  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2680
2681  owl_fmtext_append_normal(&fm,"magenta:  ");
2682  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2683
2684  owl_fmtext_append_normal(&fm,"cyan:     ");
2685  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2686
2687  owl_fmtext_append_normal(&fm,"white:    ");
2688  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2689
2690  for(i = 8; i < COLORS; ++i) {
2691    char* str1 = owl_sprintf("%4i:     ",i);
2692    char* str2 = owl_sprintf("%i\n",i);
2693    owl_fmtext_append_normal(&fm,str1);
2694    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2695    owl_free(str1);
2696     owl_free(str2);
2697  }
2698 
2699  owl_function_popless_fmtext(&fm);
2700  owl_fmtext_cleanup(&fm);
2701}
2702
2703/* add the given class, inst, recip to the punt list for filtering.
2704 *   if direction==0 then punt
2705 *   if direction==1 then unpunt
2706 */
2707void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2708{
2709  char *puntexpr, *classexpr, *instexpr, *recipexpr;
2710  char *quoted;
2711
2712  if (!strcmp(class, "*")) {
2713    classexpr = owl_sprintf("class .*");
2714  } else {
2715    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2716    owl_text_tr(quoted, ' ', '.');
2717    owl_text_tr(quoted, '\'', '.');
2718    owl_text_tr(quoted, '"', '.');
2719    classexpr = owl_sprintf("class ^(un)*%s(\\.d)*$", quoted);
2720    owl_free(quoted);
2721  }
2722  if (!strcmp(inst, "*")) {
2723    instexpr = owl_sprintf(" and instance .*");
2724  } else {
2725    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2726    owl_text_tr(quoted, ' ', '.');
2727    owl_text_tr(quoted, '\'', '.');
2728    owl_text_tr(quoted, '"', '.');
2729    instexpr = owl_sprintf(" and instance ^(un)*%s(\\.d)*$", quoted);
2730    owl_free(quoted);
2731  }
2732  if (!strcmp(recip, "*")) {
2733    recipexpr = owl_sprintf("");
2734  } else {
2735    if(!strcmp(recip, "%me%")) {
2736      recip = owl_zephyr_get_sender();
2737    }
2738    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2739    owl_text_tr(quoted, ' ', '.');
2740    owl_text_tr(quoted, '\'', '.');
2741    owl_text_tr(quoted, '"', '.');
2742    recipexpr = owl_sprintf(" and recipient ^%s$", quoted);
2743    owl_free(quoted);
2744  }
2745
2746  puntexpr = owl_sprintf("%s %s %s", classexpr, instexpr, recipexpr);
2747  owl_function_punt(puntexpr, direction);
2748  owl_free(puntexpr);
2749  owl_free(classexpr);
2750  owl_free(instexpr);
2751  owl_free(recipexpr);
2752}
2753
2754void owl_function_punt(const char *filter, int direction)
2755{
2756  owl_filter *f;
2757  owl_list *fl;
2758  int i, j;
2759  fl=owl_global_get_puntlist(&g);
2760
2761  /* first, create the filter */
2762  owl_function_debugmsg("About to filter %s", filter);
2763  f = owl_filter_new_fromstring("punt-filter", filter);
2764  if (f == NULL) {
2765    owl_function_error("Error creating filter for zpunt");
2766    return;
2767  }
2768
2769  /* Check for an identical filter */
2770  j=owl_list_get_size(fl);
2771  for (i=0; i<j; i++) {
2772    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
2773      owl_function_debugmsg("found an equivalent punt filter");
2774      /* if we're punting, then just silently bow out on this duplicate */
2775      if (direction==0) {
2776        owl_filter_delete(f);
2777        return;
2778      }
2779
2780      /* if we're unpunting, then remove this filter from the puntlist */
2781      if (direction==1) {
2782        owl_filter_delete(owl_list_get_element(fl, i));
2783        owl_list_remove_element(fl, i);
2784        owl_filter_delete(f);
2785        return;
2786      }
2787    }
2788  }
2789
2790  owl_function_debugmsg("punting");
2791  /* If we're punting, add the filter to the global punt list */
2792  if (direction==0) {
2793    owl_list_append_element(fl, f);
2794  }
2795}
2796
2797void owl_function_show_keymaps(void)
2798{
2799  owl_list l;
2800  owl_fmtext fm;
2801  const owl_keymap *km;
2802  const owl_keyhandler *kh;
2803  int i, numkm;
2804  const char *kmname;
2805
2806  kh = owl_global_get_keyhandler(&g);
2807  owl_fmtext_init_null(&fm);
2808  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2809  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2810  owl_keyhandler_get_keymap_names(kh, &l);
2811  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2812  owl_fmtext_append_normal(&fm, "\n");
2813
2814  numkm = owl_list_get_size(&l);
2815  for (i=0; i<numkm; i++) {
2816    kmname = owl_list_get_element(&l, i);
2817    km = owl_keyhandler_get_keymap(kh, kmname);
2818    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2819    owl_keymap_get_details(km, &fm, 0);
2820  }
2821  owl_fmtext_append_normal(&fm, "\n");
2822 
2823  owl_function_popless_fmtext(&fm);
2824  owl_keyhandler_keymap_namelist_cleanup(&l);
2825  owl_fmtext_cleanup(&fm);
2826}
2827
2828char *owl_function_keymap_summary(const char *name)
2829{
2830  const owl_keymap *km
2831    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2832  if (km) return owl_keymap_summary(km);
2833  else return(NULL);
2834}
2835
2836/* TODO: implement for real */
2837void owl_function_show_keymap(const char *name)
2838{
2839  owl_fmtext fm;
2840  const owl_keymap *km;
2841
2842  owl_fmtext_init_null(&fm);
2843  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2844  if (km) {
2845    owl_keymap_get_details(km, &fm, 1);
2846  } else {
2847    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2848  } 
2849  owl_function_popless_fmtext(&fm);
2850  owl_fmtext_cleanup(&fm);
2851}
2852
2853void owl_function_help_for_command(const char *cmdname)
2854{
2855  owl_fmtext fm;
2856
2857  owl_fmtext_init_null(&fm);
2858  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2859  owl_function_popless_fmtext(&fm); 
2860  owl_fmtext_cleanup(&fm);
2861}
2862
2863void owl_function_search_start(const char *string, int direction)
2864{
2865  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS or
2866   * OWL_DIRECTION_NONE */
2867  owl_regex re;
2868
2869  if (string && owl_regex_create_quoted(&re, string) == 0) {
2870    owl_global_set_search_re(&g, &re);
2871    owl_regex_cleanup(&re);
2872  } else {
2873    owl_global_set_search_re(&g, NULL);
2874  }
2875
2876  if (direction == OWL_DIRECTION_NONE)
2877    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2878  else
2879    owl_function_search_helper(0, direction);
2880}
2881
2882void owl_function_search_continue(int direction)
2883{
2884  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
2885  owl_function_search_helper(1, direction);
2886}
2887
2888void owl_function_search_helper(int mode, int direction)
2889{
2890  /* move to a message that contains the string.  If direction is
2891   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2892   * OWL_DIRECTION_UPWARDS then search backwards.
2893   *
2894   * If mode==0 then it will stay on the current message if it
2895   * contains the string.
2896   */
2897
2898  const owl_view *v;
2899  int viewsize, i, curmsg, start;
2900  owl_message *m;
2901
2902  v=owl_global_get_current_view(&g);
2903  viewsize=owl_view_get_size(v);
2904  curmsg=owl_global_get_curmsg(&g);
2905 
2906  if (viewsize==0) {
2907    owl_function_error("No messages present");
2908    return;
2909  }
2910
2911  if (mode==0) {
2912    start=curmsg;
2913  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2914    start=curmsg+1;
2915  } else {
2916    start=curmsg-1;
2917  }
2918
2919  /* bounds check */
2920  if (start>=viewsize || start<0) {
2921    owl_function_error("No further matches found");
2922    return;
2923  }
2924
2925  for (i=start; i<viewsize && i>=0;) {
2926    m=owl_view_get_element(v, i);
2927    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2928      owl_global_set_curmsg(&g, i);
2929      owl_function_calculate_topmsg(direction);
2930      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2931      if (direction==OWL_DIRECTION_DOWNWARDS) {
2932        owl_global_set_direction_downwards(&g);
2933      } else {
2934        owl_global_set_direction_upwards(&g);
2935      }
2936      return;
2937    }
2938    if (direction==OWL_DIRECTION_DOWNWARDS) {
2939      i++;
2940    } else {
2941      i--;
2942    }
2943    owl_function_mask_sigint(NULL);
2944    if(owl_global_is_interrupted(&g)) {
2945      owl_global_unset_interrupted(&g);
2946      owl_function_unmask_sigint(NULL);
2947      owl_function_makemsg("Search interrupted!");
2948      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2949      return;
2950    }
2951    owl_function_unmask_sigint(NULL);
2952  }
2953  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2954  owl_function_error("No matches found");
2955}
2956
2957/* strips formatting from ztext and returns the unformatted text.
2958 * caller is responsible for freeing. */
2959char *owl_function_ztext_stylestrip(const char *zt)
2960{
2961  owl_fmtext fm;
2962  char *plaintext;
2963
2964  owl_fmtext_init_null(&fm);
2965  owl_fmtext_append_ztext(&fm, zt);
2966  plaintext = owl_fmtext_print_plain(&fm);
2967  owl_fmtext_cleanup(&fm);
2968  return(plaintext);
2969}
2970
2971/* Popup a buddylisting.  If filename is NULL use the default .anyone */
2972void owl_function_buddylist(int aim, int zephyr, const char *filename)
2973{
2974  int i, j, idle;
2975  int interrupted = 0;
2976  owl_fmtext fm;
2977  const owl_buddylist *bl;
2978  const owl_buddy *b;
2979  char *timestr;
2980#ifdef HAVE_LIBZEPHYR
2981  int x;
2982  owl_list anyone;
2983  const char *user;
2984  char *tmp;
2985  ZLocations_t location[200];
2986  int numlocs, ret;
2987#endif
2988
2989  owl_fmtext_init_null(&fm);
2990
2991  /* AIM first */
2992  if (aim && owl_global_is_aimloggedin(&g)) {
2993    bl=owl_global_get_buddylist(&g);
2994
2995    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
2996    /* we're assuming AIM for now */
2997    j=owl_buddylist_get_size(bl);
2998    for (i=0; i<j; i++) {
2999      b=owl_buddylist_get_buddy_n(bl, i);
3000      idle=owl_buddy_get_idle_time(b);
3001      if (idle!=0) {
3002        timestr=owl_util_minutes_to_timestr(idle);
3003      } else {
3004        timestr=owl_strdup("");
3005      }
3006      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3007      owl_free(timestr);
3008    }
3009  }
3010
3011#ifdef HAVE_LIBZEPHYR
3012  if (zephyr) {
3013    if(!owl_global_is_havezephyr(&g)) {
3014      owl_function_error("Zephyr currently not available.");
3015    } else {
3016      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3017      owl_list_create(&anyone);
3018      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3019      if (ret) {
3020        if (errno == ENOENT) {
3021          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3022          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3023          owl_fmtext_append_bold(  &fm, "<username>");
3024          owl_fmtext_append_normal(&fm, "'.\n");
3025        } else {
3026          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3027        }
3028      } else {
3029        j=owl_list_get_size(&anyone);
3030        for (i=0; i<j; i++) {
3031          user=owl_list_get_element(&anyone, i);
3032          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3033
3034          owl_function_mask_sigint(NULL);
3035          if(owl_global_is_interrupted(&g)) {
3036            interrupted = 1;
3037            owl_global_unset_interrupted(&g);
3038            owl_function_unmask_sigint(NULL);
3039            owl_function_makemsg("Interrupted!");
3040            break;
3041          }
3042
3043          owl_function_unmask_sigint(NULL);
3044
3045          if (ret!=ZERR_NONE) {
3046            owl_function_error("Error getting location for %s", user);
3047            continue;
3048          }
3049
3050          numlocs=200;
3051          ret=ZGetLocations(location, &numlocs);
3052          if (ret==0) {
3053            for (x=0; x<numlocs; x++) {
3054              tmp=short_zuser(user);
3055              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3056                                        tmp,
3057                                        location[x].host,
3058                                        location[x].tty,
3059                                        location[x].time);
3060              owl_free(tmp);
3061            }
3062            if (numlocs>=200) {
3063              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3064            }
3065          }
3066        }
3067      }
3068      owl_list_cleanup(&anyone, owl_free);
3069    }
3070  }
3071#endif
3072
3073  if (aim && zephyr) {
3074    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3075      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3076      if (perlblist) {
3077        owl_fmtext_append_ztext(&fm, perlblist);
3078        owl_free(perlblist);
3079      }
3080    }
3081  }
3082
3083  if(!interrupted) {
3084    owl_function_popless_fmtext(&fm);
3085  }
3086  owl_fmtext_cleanup(&fm);
3087}
3088
3089/* Dump messages in the current view to the file 'filename'. */
3090void owl_function_dump(const char *filename) 
3091{
3092  int i, j;
3093  owl_message *m;
3094  const owl_view *v;
3095  FILE *file;
3096  char *plaintext;
3097
3098  v=owl_global_get_current_view(&g);
3099
3100  /* in the future make it ask yes/no */
3101  /*
3102  ret=stat(filename, &sbuf);
3103  if (!ret) {
3104    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3105    if (!ret) return;
3106  }
3107  */
3108
3109  file=fopen(filename, "w");
3110  if (!file) {
3111    owl_function_error("Error opening file");
3112    return;
3113  }
3114
3115  j=owl_view_get_size(v);
3116  for (i=0; i<j; i++) {
3117    m=owl_view_get_element(v, i);
3118    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3119    if (plaintext) {
3120      fputs(plaintext, file);
3121      owl_free(plaintext);
3122    }
3123  }
3124  fclose(file);
3125  owl_function_makemsg("Messages dumped to %s", filename);
3126}
3127
3128void owl_function_do_newmsgproc(void)
3129{
3130  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3131    /* if there's a process out there, we need to check on it */
3132    if (owl_global_get_newmsgproc_pid(&g)) {
3133      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3134      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3135      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3136      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3137        /* it exited */
3138        owl_global_set_newmsgproc_pid(&g, 0);
3139        owl_function_debugmsg("newmsgproc exited");
3140      } else {
3141        owl_function_debugmsg("newmsgproc did not exit");
3142      }
3143    }
3144   
3145    /* if it exited, fork & exec a new one */
3146    if (owl_global_get_newmsgproc_pid(&g)==0) {
3147      pid_t i;
3148      int myargc;
3149      i=fork();
3150      if (i) {
3151        /* parent set the child's pid */
3152        owl_global_set_newmsgproc_pid(&g, i);
3153        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3154      } else {
3155        /* child exec's the program */
3156        char **parsed;
3157        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3158        if (myargc < 0) {
3159          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3160        }
3161        if (myargc <= 0) {
3162          _exit(127);
3163        }
3164        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3165        parsed[myargc] = NULL;
3166       
3167        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3168       
3169        execvp(parsed[0], parsed);
3170       
3171       
3172        /* was there an error exec'ing? */
3173        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3174                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3175        _exit(127);
3176      }
3177    }
3178  }
3179}
3180
3181/* print the xterm escape sequence to raise the window */
3182void owl_function_xterm_raise(void)
3183{
3184  printf("\033[5t");
3185}
3186
3187/* print the xterm escape sequence to deiconify the window */
3188void owl_function_xterm_deiconify(void)
3189{
3190  printf("\033[1t");
3191}
3192
3193/* Add the specified command to the startup file.  Eventually this
3194 * should be clever, and rewriting settings that will obviosly
3195 * override earlier settings with 'set' 'bindkey' and 'alias'
3196 * commands.  For now though we just remove any line that would
3197 * duplicate this one and then append this line to the end of
3198 * startupfile.
3199 */
3200void owl_function_addstartup(const char *buff)
3201{
3202  FILE *file;
3203  const char *filename;
3204
3205  filename=owl_global_get_startupfile(&g);
3206
3207  /* delete earlier copies */
3208  owl_util_file_deleteline(filename, buff, 1);
3209
3210  file=fopen(filename, "a");
3211  if (!file) {
3212    owl_function_error("Error opening startupfile for new command");
3213    return;
3214  }
3215
3216  /* add this line */
3217  fprintf(file, "%s\n", buff);
3218
3219  fclose(file);
3220}
3221
3222/* Remove the specified command from the startup file. */
3223void owl_function_delstartup(const char *buff)
3224{
3225  const char *filename;
3226  filename=owl_global_get_startupfile(&g);
3227  owl_util_file_deleteline(filename, buff, 1);
3228}
3229
3230/* Execute owl commands from the given filename.  If the filename
3231 * is NULL, use the default owl startup commands file.
3232 */
3233void owl_function_source(const char *filename)
3234{
3235  char *path;
3236  FILE *file;
3237  char *s = NULL;
3238  int fail_silent = 0;
3239
3240  if (!filename) {
3241    fail_silent = 1;
3242    path = owl_strdup(owl_global_get_startupfile(&g));
3243  } else {
3244    path = owl_util_makepath(filename);
3245  }
3246  file = fopen(path, "r");
3247  owl_free(path);
3248  if (!file) {
3249    if (!fail_silent) {
3250      owl_function_error("Error opening file: %s", filename);
3251    }
3252    return;
3253  }
3254  while (owl_getline_chomp(&s, file)) {
3255    if (s[0] == '\0' || s[0] == '#')
3256      continue;
3257    owl_function_command(s);
3258  }
3259
3260  owl_free(s);
3261  fclose(file);
3262}
3263
3264void owl_function_change_style(owl_view *v, const char *stylename)
3265{
3266  const owl_style *s;
3267
3268  s=owl_global_get_style_by_name(&g, stylename);
3269  if (!s) {
3270    owl_function_error("No style named %s", stylename);
3271    return;
3272  }
3273  owl_view_set_style(v, s);
3274  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3275  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3276  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3277}
3278
3279void owl_function_toggleoneline(void)
3280{
3281  owl_view *v;
3282  const owl_style *s;
3283
3284  v=owl_global_get_current_view(&g);
3285  s=owl_view_get_style(v);
3286
3287  if (!owl_style_matches_name(s, "oneline")) {
3288    owl_function_change_style(v, "oneline");
3289  } else {
3290    owl_function_change_style(v, owl_global_get_default_style(&g));
3291  }
3292
3293  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3294  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3295  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3296}
3297
3298void owl_function_error(const char *fmt, ...)
3299{
3300  static int in_error = 0;
3301  va_list ap;
3302  char *buff;
3303  const char *nl;
3304
3305  if (++in_error > 2) {
3306    /* More than two nested errors, bail immediately. */
3307    in_error--;
3308    return;
3309  }
3310
3311  va_start(ap, fmt);
3312  buff = g_strdup_vprintf(fmt, ap);
3313  va_end(ap);
3314
3315  owl_function_debugmsg("ERROR: %s", buff);
3316  owl_function_log_err(buff);
3317
3318  nl = strchr(buff, '\n');
3319
3320  /*
3321    Showing admin messages triggers a lot of code. If we have a
3322    recursive error call, that's the most likely candidate, so
3323    suppress the call in that case, to try to avoid infinite looping.
3324  */
3325
3326  if(nl && *(nl + 1) && in_error == 1) {
3327    /* Multiline error */
3328    owl_function_adminmsg("ERROR", buff);
3329  } else {
3330    owl_function_makemsg("[Error] %s", buff);
3331  }
3332
3333  owl_free(buff);
3334
3335  in_error--;
3336}
3337
3338void owl_function_log_err(const char *string)
3339{
3340  char *date;
3341  time_t now;
3342  char *buff;
3343
3344  now=time(NULL);
3345  date=owl_strdup(ctime(&now));
3346  date[strlen(date)-1]='\0';
3347
3348  buff = owl_sprintf("%s %s", date, string);
3349
3350  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3351
3352  owl_free(buff);
3353  owl_free(date);
3354}
3355
3356void owl_function_showerrs(void)
3357{
3358  owl_fmtext fm;
3359
3360  owl_fmtext_init_null(&fm);
3361  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3362  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3363  owl_function_popless_fmtext(&fm);
3364}
3365
3366void owl_function_makemsg(const char *fmt, ...)
3367{
3368  va_list ap;
3369  char *str;
3370
3371  va_start(ap, fmt);
3372  str = g_strdup_vprintf(fmt, ap);
3373  va_end(ap);
3374
3375  owl_function_debugmsg("makemsg: %s", str);
3376  owl_msgwin_set_text(&g.msgwin, str);
3377}
3378
3379/* get locations for everyone in .anyone.  If 'notify' is '1' then
3380 * send a pseudo login or logout message for everyone not in sync with
3381 * the global zephyr buddy list.  The list is updated regardless of
3382 * the status of 'notify'.
3383 */
3384void owl_function_zephyr_buddy_check(int notify)
3385{
3386#ifdef HAVE_LIBZEPHYR
3387  int i, j;
3388  owl_list anyone;
3389  owl_zbuddylist *zbl;
3390  GList **zaldlist;
3391  GList *zaldptr;
3392  ZAsyncLocateData_t *zald;
3393  const char *user;
3394
3395  if (!owl_global_is_havezephyr(&g)) return;
3396  owl_global_set_pseudologin_notify(&g, notify);
3397  zbl = owl_global_get_zephyr_buddylist(&g);
3398  zaldlist = owl_global_get_zaldlist(&g);
3399
3400  /* Clear the existing ZALDs first. */
3401  zaldptr = g_list_first(*zaldlist);
3402  while (zaldptr) {
3403    ZFreeALD(zaldptr->data);
3404    owl_free(zaldptr->data);
3405    zaldptr = g_list_next(zaldptr);
3406  }
3407  g_list_free(*zaldlist);
3408  *zaldlist = NULL;
3409
3410  owl_list_create(&anyone);
3411  owl_zephyr_get_anyone_list(&anyone, NULL);
3412  j = owl_list_get_size(&anyone);
3413  for (i = 0; i < j; i++) {
3414    user = owl_list_get_element(&anyone, i);
3415    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
3416    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3417      *zaldlist = g_list_append(*zaldlist, zald);
3418    } else {
3419      owl_free(zald);
3420    }
3421  }
3422
3423  owl_list_cleanup(&anyone, owl_free);
3424#endif
3425}
3426
3427void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3428{
3429  owl_fmtext fm;
3430  int i, j;
3431
3432  owl_fmtext_init_null(&fm);
3433  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3434  owl_fmtext_append_normal(&fm, email);
3435  owl_fmtext_append_normal(&fm, ":\n");
3436
3437  j=owl_list_get_size(namelist);
3438  for (i=0; i<j; i++) {
3439    owl_fmtext_append_normal(&fm, "  ");
3440    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3441    owl_fmtext_append_normal(&fm, "\n");
3442  }
3443
3444  owl_function_popless_fmtext(&fm);
3445  owl_fmtext_cleanup(&fm);
3446}
3447
3448int owl_function_get_color_count(void)
3449{
3450     return COLORS;
3451}
3452
3453void owl_function_mask_sigint(sigset_t *oldmask) {
3454  sigset_t intr;
3455
3456  sigemptyset(&intr);
3457  sigaddset(&intr, SIGINT);
3458  sigprocmask(SIG_BLOCK, &intr, oldmask);
3459}
3460
3461void owl_function_unmask_sigint(sigset_t *oldmask) {
3462  sigset_t intr;
3463
3464  sigemptyset(&intr);
3465  sigaddset(&intr, SIGINT);
3466  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
3467}
3468
3469void _owl_function_mark_message(const owl_message *m)
3470{
3471  if (m) {
3472    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3473    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3474  }
3475}
3476
3477void owl_function_mark_message(void)
3478{
3479  const owl_message *m;
3480  const owl_view *v;
3481
3482  v=owl_global_get_current_view(&g);
3483
3484  /* bail if there's no current message */
3485  if (owl_view_get_size(v) < 1) {
3486    owl_function_error("No messages to mark");
3487    return;
3488  }
3489
3490  /* mark the message */
3491  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3492  _owl_function_mark_message(m);
3493  owl_function_makemsg("Mark set");
3494}
3495
3496void owl_function_swap_cur_marked(void)
3497{
3498  int marked_id;
3499  const owl_message *m;
3500  const owl_view *v;
3501
3502  marked_id=owl_global_get_markedmsgid(&g);
3503  if (marked_id == -1) {
3504    owl_function_error("Mark not set.");
3505    return;
3506  }
3507
3508  v=owl_global_get_current_view(&g);
3509  /* bail if there's no current message */
3510  if (owl_view_get_size(v) < 1) {
3511    return;
3512  }
3513
3514  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3515  _owl_function_mark_message(m);
3516  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3517  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3518  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3519  owl_global_set_direction_downwards(&g);
3520}
Note: See TracBrowser for help on using the repository browser.