comparison src/protocols/msn/msn.c @ 4150:1bd663beada5

[gaim-migrate @ 4369] potato chip file receive in msn committer: Tailor Script <tailor@pidgin.im>
author Rob Flynn <gaim@robflynn.com>
date Sat, 28 Dec 2002 05:15:43 +0000
parents 474265997752
children 9615c6996348
comparison
equal deleted inserted replaced
4149:baf9d94e0128 4150:1bd663beada5
6 #include <winsock.h> 6 #include <winsock.h>
7 #include <io.h> 7 #include <io.h>
8 #endif 8 #endif
9 9
10 10
11 #include <sys/stat.h>
11 #include <stdlib.h> 12 #include <stdlib.h>
12 #include <string.h> 13 #include <string.h>
13 #include <errno.h> 14 #include <errno.h>
14 #include <stdio.h> 15 #include <stdio.h>
15 #include <ctype.h> 16 #include <ctype.h>
56 #define MSN_PORT 1863 57 #define MSN_PORT 1863
57 58
58 #define MSN_TYPING_RECV_TIMEOUT 6 59 #define MSN_TYPING_RECV_TIMEOUT 6
59 #define MSN_TYPING_SEND_TIMEOUT 4 60 #define MSN_TYPING_SEND_TIMEOUT 4
60 61
61 62 struct msn_file_transfer {
62 63 enum { MFT_SENDFILE_IN, MFT_SENDFILE_OUT } type;
63 struct msn_data { 64 struct file_transfer *xfer;
65 struct gaim_connection *gc;
66
64 int fd; 67 int fd;
65 int trId;
66 int inpa; 68 int inpa;
69
70 char *filename;
71
72 char *sn;
73 char ip[16];
74 int port;
75
76 unsigned long cookie;
77 unsigned long authcookie;
78
79 int len;
67 80
68 char *rxqueue; 81 char *rxqueue;
69 int rxlen; 82 int rxlen;
70 gboolean msg; 83 gboolean msg;
71 char *msguser; 84 char *msguser;
72 int msglen; 85 int msglen;
86 };
87
88 struct msn_data {
89 int fd;
90 int trId;
91 int inpa;
92
93 char *rxqueue;
94 int rxlen;
95 gboolean msg;
96 char *msguser;
97 int msglen;
73 98
74 GSList *switches; 99 GSList *switches;
75 GSList *fl; 100 GSList *fl;
76 GSList *permit; 101 GSList *permit;
77 GSList *deny; 102 GSList *deny;
103 GSList *file_transfers;
78 104
79 char *kv; 105 char *kv;
80 char *sid; 106 char *sid;
81 char *mspauth; 107 char *mspauth;
82 unsigned long sl; 108 unsigned long sl;
109 char *friend; 135 char *friend;
110 }; 136 };
111 137
112 static void msn_login_callback(gpointer, gint, GaimInputCondition); 138 static void msn_login_callback(gpointer, gint, GaimInputCondition);
113 static void msn_login_xfr_connect(gpointer, gint, GaimInputCondition); 139 static void msn_login_xfr_connect(gpointer, gint, GaimInputCondition);
140 static struct msn_file_transfer *find_mft_by_cookie(struct gaim_connection *gc,
141 unsigned long cookie);
142 static struct msn_file_transfer *find_mft_by_xfer(struct gaim_connection *gc,
143 struct file_transfer *xfer);
114 144
115 #define GET_NEXT(tmp) while (*(tmp) && *(tmp) != ' ') \ 145 #define GET_NEXT(tmp) while (*(tmp) && *(tmp) != ' ') \
116 (tmp)++; \ 146 (tmp)++; \
117 *(tmp)++ = 0; \ 147 *(tmp)++ = 0; \
118 while (*(tmp) && *(tmp) == ' ') \ 148 while (*(tmp) && *(tmp) == ' ') \
595 625
596 cur = url_decode(ret->str); 626 cur = url_decode(ret->str);
597 g_string_free(ret, TRUE); 627 g_string_free(ret, TRUE);
598 return cur; 628 return cur;
599 } 629 }
600 630
601 631 static int msn_process_msnftp(struct msn_file_transfer *mft, char *buf)
632 {
633 struct gaim_connection *gc = mft->gc;
634 char sendbuf[MSN_BUF_LEN];
635
636 if (!g_strncasecmp(buf, "VER MSNFTP", 10)) {
637
638 /* Send the USR string. */
639 g_snprintf(sendbuf, sizeof(sendbuf), "USR %s %ld\r\n",
640 gc->username, mft->authcookie);
641
642 if (msn_write(mft->fd, sendbuf, strlen(sendbuf)) < 0) {
643 /* TODO: Clean up */
644 return 0;
645 }
646 }
647 else if (!g_strncasecmp(buf, "FIL", 3)) {
648
649 char *tmp = buf;
650
651 GET_NEXT(tmp);
652
653 mft->len = atoi(tmp);
654
655 /* Send the TFR string, to request a start of transfer. */
656 g_snprintf(sendbuf, sizeof(sendbuf), "TFR\r\n");
657
658 gaim_input_remove(mft->inpa);
659 mft->inpa = 0;
660
661 if (msn_write(mft->fd, sendbuf, strlen(sendbuf)) < 0) {
662 /* TODO: Clean up */
663 return 0;
664 }
665
666 if (transfer_in_do(mft->xfer, mft->fd, mft->filename, mft->len)) {
667 debug_printf("MSN: transfer_in_do failed\n");
668 }
669 }
670
671 return 1;
672 }
673
674 static void msn_msnftp_callback(gpointer data, gint source,
675 GaimInputCondition cond)
676 {
677 struct msn_file_transfer *mft = (struct msn_file_transfer *)data;
678 char buf[MSN_BUF_LEN];
679 int cont = 1;
680 int len;
681
682 if (mft->fd != source)
683 mft->fd = source;
684
685 len = read(mft->fd, buf, sizeof(buf));
686
687 if (len <= 0) {
688 /* TODO: Kill mft. */
689 return;
690 }
691
692 mft->rxqueue = g_realloc(mft->rxqueue, len + mft->rxlen);
693 memcpy(mft->rxqueue + mft->rxlen, buf, len);
694 mft->rxlen += len;
695
696 while (cont) {
697 char *end = mft->rxqueue;
698 int cmdlen;
699 char *cmd;
700 int i = 0;
701
702 if (!mft->rxlen)
703 return;
704
705 while (i + 1 < mft->rxlen) {
706 if (*end == '\r' && end[1] == '\n')
707 break;
708 end++; i++;
709 }
710 if (i + 1 == mft->rxlen)
711 return;
712
713 cmdlen = end - mft->rxqueue + 2;
714 cmd = mft->rxqueue;
715 mft->rxlen -= cmdlen;
716 if (mft->rxlen) {
717 mft->rxqueue = g_memdup(cmd + cmdlen, mft->rxlen);
718 } else {
719 mft->rxqueue = NULL;
720 cmd = g_realloc(cmd, cmdlen + 1);
721 }
722 cmd[cmdlen] = 0;
723
724 g_strchomp(cmd);
725 cont = msn_process_msnftp(mft, cmd);
726
727 g_free(cmd);
728 }
729 }
730
731 static void msn_msnftp_connect(gpointer data, gint source,
732 GaimInputCondition cond)
733 {
734 struct msn_file_transfer *mft = (struct msn_file_transfer *)data;
735 struct gaim_connection *gc = mft->gc;
736 char buf[MSN_BUF_LEN];
737
738 if (source == -1 || !g_slist_find(connections, gc)) {
739 debug_printf("Error establishing MSNFTP connection\n");
740 close(source);
741 /* TODO: Clean up */
742 return;
743 }
744
745 if (mft->fd != source)
746 mft->fd = source;
747
748 g_snprintf(buf, sizeof(buf), "VER MSNFTP\r\n");
749
750 if (msn_write(mft->fd, buf, strlen(buf)) < 0) {
751 /* TODO: Clean up */
752 return;
753 }
754
755 mft->inpa = gaim_input_add(mft->fd, GAIM_INPUT_READ,
756 msn_msnftp_callback, mft);
757 }
758
759 static void msn_process_ft_msg(struct msn_switchboard *ms, char *msg)
760 {
761 struct msn_file_transfer *mft;
762 struct msn_data *md = ms->gc->proto_data;
763 char *tmp = msg;
764
765 if (strstr(msg, "Application-Name: File Transfer") &&
766 strstr(msg, "Invitation-Command: INVITE")) {
767
768 /*
769 * First invitation message, requesting an ACCEPT or CANCEL from
770 * the recipient. Used in incoming file transfers.
771 */
772
773 char *filename;
774 char *cookie_s, *filesize_s;
775 size_t filesize;
776
777 tmp = strstr(msg, "Invitation-Cookie");
778 GET_NEXT(tmp);
779 cookie_s = tmp;
780 GET_NEXT(tmp);
781 GET_NEXT(tmp);
782 filename = tmp;
783
784 /* Needed for filenames with spaces */
785 tmp = strchr(tmp, '\r');
786 *tmp = '\0';
787 tmp += 2;
788
789 GET_NEXT(tmp);
790 filesize_s = tmp;
791 GET_NEXT(tmp);
792
793 mft = g_new0(struct msn_file_transfer, 1);
794 mft->gc = ms->gc;
795 mft->type = MFT_SENDFILE_IN;
796 mft->sn = g_strdup(ms->msguser);
797 mft->cookie = atoi(cookie_s);
798 mft->filename = g_strdup(filename);
799
800 filesize = atoi(filesize_s);
801
802 md->file_transfers = g_slist_append(md->file_transfers, mft);
803
804 mft->xfer = transfer_in_add(ms->gc, ms->msguser,
805 mft->filename, filesize, 1, NULL);
806 }
807 else if (strstr(msg, "Invitation-Command: ACCEPT")) {
808
809 /*
810 * XXX I hope these checks don't return false positives, but they
811 * seem like they should work. The only issue is alternative
812 * protocols, *maybe*.
813 */
814
815 if (strstr(msg, "AuthCookie:")) {
816
817 /*
818 * Second invitation request, sent after the recipient accepts
819 * the request. Used in incoming file transfers.
820 */
821
822 char *cookie_s, *ip, *port_s, *authcookie_s;
823
824 tmp = strstr(msg, "Invitation-Cookie");
825 GET_NEXT(tmp);
826 cookie_s = tmp;
827 GET_NEXT(tmp);
828 GET_NEXT(tmp);
829 ip = tmp;
830 GET_NEXT(tmp);
831 GET_NEXT(tmp);
832 port_s = tmp;
833 GET_NEXT(tmp);
834 GET_NEXT(tmp);
835 authcookie_s = tmp;
836 GET_NEXT(tmp);
837
838 mft = find_mft_by_cookie(ms->gc, atoi(cookie_s));
839
840 if (!mft)
841 {
842 debug_printf("MSN: Cookie not found. File transfer aborted.\n");
843 return;
844 }
845
846 strncpy(mft->ip, ip, 16);
847 mft->port = atoi(port_s);
848 mft->authcookie = atoi(authcookie_s);
849
850 mft->fd = proxy_connect(mft->ip, mft->port, msn_msnftp_connect, mft);
851
852 if (ms->fd < 0) {
853 md->file_transfers = g_slist_remove(md->file_transfers, mft);
854 return;
855 }
856 }
857 else
858 {
859 /*
860 * An accept message from the recipient. Used in outgoing
861 * file transfers.
862 */
863 }
864 }
865 }
866
602 static void msn_process_switch_msg(struct msn_switchboard *ms, char *msg) 867 static void msn_process_switch_msg(struct msn_switchboard *ms, char *msg)
603 { 868 {
604 char *content, *agent, *format; 869 char *content, *agent, *format;
605 char *message = NULL; 870 char *message = NULL;
606 int flags = 0; 871 int flags = 0;
625 strlen( "Content-Type: text/x-msmsgscontrol\r\n"))) { 890 strlen( "Content-Type: text/x-msmsgscontrol\r\n"))) {
626 if (strstr(content,"TypingUser: ") && !ms->chat) { 891 if (strstr(content,"TypingUser: ") && !ms->chat) {
627 serv_got_typing(ms->gc, ms->msguser, MSN_TYPING_RECV_TIMEOUT, TYPING); 892 serv_got_typing(ms->gc, ms->msguser, MSN_TYPING_RECV_TIMEOUT, TYPING);
628 return; 893 return;
629 } 894 }
895
896 } else if (!g_strncasecmp(content, "Content-Type: text/x-msmsgsinvite;",
897 strlen("Content-Type: text/x-msmsgsinvite;"))) {
898
899 /*
900 * NOTE: Other things, such as voice communication, would go in
901 * here too (since they send the same Content-Type). However,
902 * this is the best check for file transfer messages, so I'm
903 * calling msn_process_ft_invite_msg(). If anybody adds support
904 * for anything else that sends a text/x-msmsgsinvite, perhaps
905 * this should be changed. For now, it stays.
906 */
907 msn_process_ft_msg(ms, content);
630 908
631 } else if (!g_strncasecmp(content, "Content-Type: text/plain", 909 } else if (!g_strncasecmp(content, "Content-Type: text/plain",
632 strlen("Content-Type: text/plain"))) { 910 strlen("Content-Type: text/plain"))) {
633 911
634 912
1773 if (msn_write(ms->fd, buf, strlen(buf)) < 0) 2051 if (msn_write(ms->fd, buf, strlen(buf)) < 0)
1774 msn_kill_switch(ms); 2052 msn_kill_switch(ms);
1775 return MSN_TYPING_SEND_TIMEOUT; 2053 return MSN_TYPING_SEND_TIMEOUT;
1776 } 2054 }
1777 2055
2056 /* XXX Don't blame me. I stole this from the oscar module! */
2057 static struct msn_file_transfer *find_mft_by_xfer(struct gaim_connection *gc,
2058 struct file_transfer *xfer)
2059 {
2060 GSList *g = ((struct msn_data *)gc->proto_data)->file_transfers;
2061 struct msn_file_transfer *f = NULL;
2062
2063 while (g) {
2064 f = (struct msn_file_transfer *)g->data;
2065 if (f->xfer == xfer)
2066 break;
2067
2068 g = g->next;
2069 f = NULL;
2070 }
2071
2072 return f;
2073 }
2074
2075 /* XXX Don't blame me. I stole this from the oscar module! */
2076 static struct msn_file_transfer *find_mft_by_cookie(struct gaim_connection *gc,
2077 unsigned long cookie)
2078 {
2079 GSList *g = ((struct msn_data *)gc->proto_data)->file_transfers;
2080 struct msn_file_transfer *f = NULL;
2081
2082 while (g) {
2083 f = (struct msn_file_transfer *)g->data;
2084 if (f->cookie == cookie)
2085 break;
2086
2087 g = g->next;
2088 f = NULL;
2089 }
2090
2091 return f;
2092 }
2093
2094 static void msn_file_transfer_cancel(struct gaim_connection *gc,
2095 struct file_transfer *xfer)
2096 {
2097 struct msn_data *md = gc->proto_data;
2098 struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
2099 struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
2100 char header[MSN_BUF_LEN];
2101 char buf[MSN_BUF_LEN];
2102
2103 if (!ms || !mft)
2104 {
2105 debug_printf("Eep! Returning from msn_file_transfer_cancel early");
2106 return;
2107 }
2108
2109 g_snprintf(header, sizeof(header),
2110 "MIME-Version: 1.0\r\n"
2111 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n"
2112 "Invitation-Command: CANCEL\r\n"
2113 "Invitation-Cookie: %ld\r\n"
2114 "Cancel-Code: REJECT\r\n",
2115 mft->cookie);
2116
2117 g_snprintf(buf, sizeof(buf), "MSG %d N %d\r\n%s\r\n\r\n",
2118 ++ms->trId, strlen(header) + strlen("\r\n\r\n"),
2119 header);
2120
2121 md->file_transfers = g_slist_remove(md->file_transfers, mft);
2122
2123 if (msn_write(ms->fd, buf, strlen(buf)) < 0)
2124 {
2125 debug_printf("Uh oh! Killing switch.\n");
2126 msn_kill_switch(ms);
2127 }
2128 }
2129
2130 static void msn_file_transfer_in(struct gaim_connection *gc,
2131 struct file_transfer *xfer, int offset)
2132 {
2133 struct msn_data *md = gc->proto_data;
2134 struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
2135 struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
2136 char header[MSN_BUF_LEN];
2137 char buf[MSN_BUF_LEN];
2138
2139 if (!ms || !mft)
2140 {
2141 debug_printf("Eep! Returning from msn_file_transfer_in early");
2142 return;
2143 }
2144
2145 g_snprintf(header, sizeof(header),
2146 "MIME-Version: 1.0\r\n"
2147 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n"
2148 "Invitation-Command: ACCEPT\r\n"
2149 "Invitation-Cookie: %ld\r\n"
2150 "Launch-Application: FALSE\r\n"
2151 "Request-Data: IP-Address:\r\n",
2152 mft->cookie);
2153
2154 g_snprintf(buf, sizeof(buf), "MSG %d N %d\r\n%s\r\n\r\n",
2155 ++ms->trId, strlen(header) + strlen("\r\n\r\n"),
2156 header);
2157
2158 if (msn_write(ms->fd, buf, strlen(buf)) < 0) {
2159 msn_kill_switch(ms);
2160 return;
2161 }
2162
2163 mft->xfer = xfer;
2164 }
2165
2166 static void msn_file_transfer_out(struct gaim_connection *gc,
2167 struct file_transfer *xfer,
2168 const char *name, int totfiles, int totsize)
2169 {
2170 struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
2171 struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
2172 char header[MSN_BUF_LEN];
2173 char buf[MSN_BUF_LEN];
2174 struct stat sb;
2175
2176 if (!ms)
2177 return;
2178
2179 if (totfiles > 1)
2180 return;
2181
2182 if (stat(name, &sb) == -1)
2183 return;
2184
2185 mft->cookie = 1 + (int)(sizeof(unsigned long) * rand() / (RAND_MAX + 1.0));
2186
2187 g_snprintf(header, sizeof(header),
2188 "MIME-Version: 1.0\r\n"
2189 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
2190 "Application-Name: File Transfer\r\n"
2191 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
2192 "Invitation-Command: INVITE\r\n"
2193 "Invitation-Cookie: %ld\r\n"
2194 "Application-File: %s\r\n"
2195 "Application-FileSize: %ld\r\n",
2196 mft->cookie, name, sb.st_size);
2197
2198 g_snprintf(buf, sizeof(buf), "MSG %d A %d\r\n%s\r\n\r\n",
2199 ++ms->trId,
2200 strlen(header) + strlen("\r\n\r\n"),
2201 header);
2202
2203 if (msn_write(ms->fd, buf, strlen(buf)) < 0)
2204 msn_kill_switch(ms);
2205
2206 debug_printf("\n");
2207 }
2208
2209 static void msn_file_transfer_done(struct gaim_connection *gc,
2210 struct file_transfer *xfer)
2211 {
2212 struct msn_data *md = (struct msn_data *)gc->proto_data;
2213 struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
2214 struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
2215 char buf[MSN_BUF_LEN];
2216
2217 g_snprintf(buf, sizeof(buf), "BYE 16777989\r\n");
2218
2219 msn_write(mft->fd, buf, strlen(buf));
2220
2221 md->file_transfers = g_slist_remove(md->file_transfers, mft);
2222
2223 gaim_input_remove(mft->inpa);
2224
2225 close(mft->fd);
2226
2227 g_free(mft->filename);
2228 g_free(mft->sn);
2229 g_free(mft);
2230 }
2231
2232 static size_t msn_file_transfer_read(struct gaim_connection *gc,
2233 struct file_transfer *xfer, int fd,
2234 char **buf)
2235 {
2236 unsigned char header[3];
2237 size_t len, size;
2238
2239 if (read(fd, header, sizeof(header)) < 3)
2240 return 0;
2241
2242 if (header[0] != 0) {
2243 debug_printf("Invalid header[0]: %d. Aborting.\n", header[0]);
2244 return 0;
2245 }
2246
2247 size = header[1] | (header[2] << 8);
2248
2249 *buf = g_new0(char, size);
2250
2251 for (len = 0; len < size; len += read(fd, *buf + len, size - len));
2252
2253 return len;
2254 }
2255
1778 static int msn_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) 2256 static int msn_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags)
1779 { 2257 {
1780 struct msn_data *md = gc->proto_data; 2258 struct msn_data *md = gc->proto_data;
1781 struct msn_switchboard *ms = msn_find_switch(gc, who); 2259 struct msn_switchboard *ms = msn_find_switch(gc, who);
1782 char buf[MSN_BUF_LEN]; 2260 char buf[MSN_BUF_LEN];
1987 return; 2465 return;
1988 g_snprintf(b->show, sizeof(b->show), "%s", (char *)b->proto_data); 2466 g_snprintf(b->show, sizeof(b->show), "%s", (char *)b->proto_data);
1989 handle_buddy_rename(b, b->name); 2467 handle_buddy_rename(b, b->name);
1990 } 2468 }
1991 2469
2470 static void msn_ask_send_file(struct gaim_connection *gc, char *destsn)
2471 {
2472 struct msn_data *md = (struct msn_data *)gc->proto_data;
2473 struct msn_file_transfer *mft = g_new0(struct msn_file_transfer, 1);
2474
2475 mft->type = MFT_SENDFILE_OUT;
2476 mft->sn = g_strdup(destsn);
2477 mft->gc = gc;
2478
2479 md->file_transfers = g_slist_append(md->file_transfers, mft);
2480
2481 mft->xfer = transfer_out_add(gc, mft->sn);
2482 }
2483
1992 static GList *msn_buddy_menu(struct gaim_connection *gc, char *who) 2484 static GList *msn_buddy_menu(struct gaim_connection *gc, char *who)
1993 { 2485 {
1994 GList *m = NULL; 2486 GList *m = NULL;
1995 struct proto_buddy_menu *pbm; 2487 struct proto_buddy_menu *pbm;
1996 struct buddy *b = find_buddy(gc, who); 2488 struct buddy *b = find_buddy(gc, who);
1997 static char buf[MSN_BUF_LEN]; 2489 static char buf[MSN_BUF_LEN];
1998 2490
1999 pbm = g_new0(struct proto_buddy_menu, 1); 2491 pbm = g_new0(struct proto_buddy_menu, 1);
2000 pbm->label = _("Reset friendly name"); 2492 pbm->label = _("Reset friendly name");
2001 pbm->callback = msn_reset_friend; 2493 pbm->callback = msn_reset_friend;
2494 pbm->gc = gc;
2495 m = g_list_append(m, pbm);
2496
2497 pbm = g_new0(struct proto_buddy_menu, 1);
2498 pbm->label = _("Send File");
2499 pbm->callback = msn_ask_send_file;
2002 pbm->gc = gc; 2500 pbm->gc = gc;
2003 m = g_list_append(m, pbm); 2501 m = g_list_append(m, pbm);
2004 2502
2005 if (!b || !(b->uc >> 1)) 2503 if (!b || !(b->uc >> 1))
2006 return m; 2504 return m;
2395 ret->add_permit = msn_add_permit; 2893 ret->add_permit = msn_add_permit;
2396 ret->rem_permit = msn_rem_permit; 2894 ret->rem_permit = msn_rem_permit;
2397 ret->add_deny = msn_add_deny; 2895 ret->add_deny = msn_add_deny;
2398 ret->rem_deny = msn_rem_deny; 2896 ret->rem_deny = msn_rem_deny;
2399 ret->buddy_free = msn_buddy_free; 2897 ret->buddy_free = msn_buddy_free;
2898 ret->file_transfer_cancel = msn_file_transfer_cancel;
2899 ret->file_transfer_in = msn_file_transfer_in;
2900 ret->file_transfer_out = msn_file_transfer_out;
2901 ret->file_transfer_done = msn_file_transfer_done;
2902 ret->file_transfer_read = msn_file_transfer_read;
2400 2903
2401 puo = g_new0(struct proto_user_opt, 1); 2904 puo = g_new0(struct proto_user_opt, 1);
2402 puo->label = g_strdup(_("Server:")); 2905 puo->label = g_strdup(_("Server:"));
2403 puo->def = g_strdup(MSN_SERVER); 2906 puo->def = g_strdup(MSN_SERVER);
2404 puo->pos = USEROPT_MSNSERVER; 2907 puo->pos = USEROPT_MSNSERVER;