source: functions.c @ 0504f63

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