source: functions.c @ c43c77b

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