source: functions.c @ c4ba74d

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