source: functions.c @ bc1d648

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