source: functions.c @ f178fb5

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