source: functions.c @ 49a8434

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