Remove trailing whitespace.
[dockapps.git] / wmnotify / src / imap.c
blob8651d35d4550b36fde8031abbf7871705d0f1989
1 /*
2 * imap.c -- Routines for communication with an IMAP server
4 * Copyright (C) 2003 Hugo Villeneuve <hugo@hugovil.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
21 /* Define filename_M */
22 #define IMAP_M 1
24 #if HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <arpa/inet.h>
37 #include <ctype.h> /* for isdigit() */
39 #include "common.h"
40 #include "wmnotify.h"
41 #include "network.h"
42 #include "imap.h"
45 #define IMAP4_ENDL "\r\n" /* CRLF */
47 #define IMAP4_CMD_CAPABILITY "CAPABILITY"
48 #define IMAP4_CMD_LOGIN "LOGIN"
49 #define IMAP4_CMD_SELECT "SELECT"
50 #define IMAP4_CMD_EXAMINE "EXAMINE"
51 #define IMAP4_CMD_LOGOUT "LOGOUT"
52 #define IMAP4_CMD_SEARCH_UNSEEN "SEARCH UNSEEN"
54 /* Responses from IMAP4 server. */
55 #define IMAP4_RSP_SUCCESS "OK"
56 #define IMAP4_RSP_FAILURE "NO"
57 #define IMAP4_RSP_PROTOCOL_ERR "BAD"
58 #define IMAP4_RSP_SEARCH_UNSEEN "* SEARCH " /* This is the line that will be returned by
59 * the IMAP4 server after receiving the
60 * "SEARCH UNSEEN" command, followed by the
61 * messages ID of the unseen messages. */
64 static int tlabel = 0;
65 static int tlabel_len;
66 static int unseen_string_found;
68 /* Defined in network.c */
69 extern char tx_buffer[WMNOTIFY_BUFSIZE + 1];
70 extern char rx_buffer[WMNOTIFY_BUFSIZE + 1];
73 static int
74 IMAP4_ReceiveResponse( void )
76 int len;
77 char *token;
78 char *stringp;
80 /* All interactions transmitted by client and server are in the form of
81 lines, that is, strings that end with a CRLF. The protocol receiver
82 of an IMAP4rev1 client or server is either reading a line, or is
83 reading a sequence of octets with a known count followed by a line. */
85 get_packet:
86 len = WmnotifyGetResponse( rx_buffer, WMNOTIFY_BUFSIZE );
87 if( len < 0 ) {
88 /* An error occured. WmnotifyGetResponse() should have printed an error message. */
89 goto error;
91 else if( len == 0 ) {
92 /* The return value will be 0 when the peer has performed an orderly shutdown. */
93 if( wmnotify_infos.debug ) {
94 fprintf( stderr, "IMAP server has closed connection.\n" );
96 goto error;
98 else if( len == WMNOTIFY_BUFSIZE ) {
99 if( wmnotify_infos.debug ) {
100 ErrorLocation( __FILE__, __LINE__ );
101 fprintf( stderr, "Response too big (%d bytes) to fit in receive buffer.\n", len );
103 goto error;
106 /* We suppose that, if a partial response packet was sent, it is not broken in the middle
107 of a line (to confirm). Normally, each string is terminated by CRLF. */
108 if( STREQ_LEN( &rx_buffer[ len - 2 ], IMAP4_ENDL, 2 ) == false ) {
109 /* No CRLF found at the end of the buffer --> not handled by wmnotify. */
110 ErrorLocation( __FILE__, __LINE__ );
111 fprintf( stderr, "Response buffer doesn't contain CRLF at the end.\n" );
112 goto error;
115 if( wmnotify_infos.debug ) {
116 printf( "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" );
117 printf( "IMAP4 Server Response (size %d bytes):\n", len );
118 printf( "%s", rx_buffer );
119 printf( "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" );
122 /* Converting the last CRLF into a LF followed by a NULL termination character. */
123 rx_buffer[ len - 2 ] = '\n';
124 rx_buffer[ len - 1 ] = '\0';
126 /* Check the Server Completion Response returned by the IMAP4 server. There are currently
127 * three Server Completion Responses codes: success ("OK"), failure ("NO") and protocol error
128 * ("BAD"). */
129 stringp = rx_buffer;
131 while( ( token = strsep( &stringp, "\n" ) ) != NULL ) {
133 /* In case no delimiter was found, the token is taken to
134 be the entire string *stringp, and *stringp is made NULL. */
135 if( stringp == NULL ) {
136 if( token[0] == '\0' ) {
137 /* This means we finished parsing the last line of the buffer, but we need to
138 get more data to continue process the next part of the IMAP4 response. */
139 goto get_packet;
141 else {
142 /* This should never happen. */
143 ErrorLocation( __FILE__, __LINE__ );
144 fprintf( stderr, " Delimiter not found in strsep() call.\n" );
145 goto error;
149 if( token == NULL ) {
150 /* This should never happen. */
151 ErrorLocation( __FILE__, __LINE__ );
152 fprintf( stderr, " NULL token returned by strsep().\n" );
153 goto error;
156 if( token[0] == '*' ) {
157 /* Untagged response. If there is a space after the SEARCH response, it means
158 * at least 1 message is unseen. */
159 if( STREQ_LEN( token, IMAP4_RSP_SEARCH_UNSEEN, strlen(IMAP4_RSP_SEARCH_UNSEEN) ) == true ) {
160 unseen_string_found = true;
163 else {
164 /* Must be the status... */
166 /* We check for the correct transaction label plus a space. */
167 if( STREQ_LEN( token, tx_buffer, tlabel_len + 1 ) == true ) {
168 token += tlabel_len + 1;
169 if( STREQ_LEN( token, IMAP4_RSP_SUCCESS, strlen(IMAP4_RSP_SUCCESS) ) == true ) {
170 goto end; /* OK, no errors. */
172 else if( STREQ_LEN( token, IMAP4_RSP_PROTOCOL_ERR, strlen(IMAP4_RSP_PROTOCOL_ERR) ) == true ) {
173 fprintf( stderr, "%s: Protocol error (%s).\n", PACKAGE, token );
174 goto error;
176 else if( STREQ_LEN( token, IMAP4_RSP_FAILURE, strlen(IMAP4_RSP_FAILURE) ) == true ) {
177 fprintf( stderr, "%s: Failure (%s).\n", PACKAGE, token );
178 goto error;
180 else {
181 fprintf( stderr, "%s: Unknown error code (%s).\n", PACKAGE, token );
182 goto error;
185 else {
186 fprintf( stderr, "%s: Error, transaction label mismatch.\n", PACKAGE );
187 goto error;
190 } /* while( token ) */
192 /* Get next part of IMAP4 response. */
193 goto get_packet;
195 end:
196 /* No error. */
197 return len;
199 error:
200 return -1;
204 static int
205 IMAP4_SendCommand( int argc, char *argv[] )
207 int len;
208 int i;
210 /* Adding Transaction Label. */
211 tlabel++;
212 tx_buffer[0] = 'A';
213 len = 1;
214 len += sprintf( tx_buffer + len, "%d", tlabel );
215 tlabel_len = len;
217 /* Adding command and it's arguments. */
218 for( i = 0; i < argc; i++ ) {
219 len += sprintf( tx_buffer + len, " %s", argv[i] );
222 if( wmnotify_infos.debug ) {
223 tx_buffer[len] = '\0';
224 printf( ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" );
225 printf( "IMAP4 Client Command (size %d bytes):\n%s\n", len, tx_buffer );
226 printf( ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" );
229 /* Adding termination characters. */
230 len += sprintf( tx_buffer + len, IMAP4_ENDL );
232 len = WmnotifySendData( tx_buffer, len );
233 if( len < 0 ) {
234 return EXIT_FAILURE;
237 len = IMAP4_ReceiveResponse();
238 if( len < 0 ) {
239 return EXIT_FAILURE;
242 return EXIT_SUCCESS;
247 IMAP4_CheckForNewMail( void )
249 char *argv[10];
250 int new_messages = 0;
251 int status;
253 status = ConnectionEstablish( wmnotify_infos.server_name, wmnotify_infos.port );
254 if( status != EXIT_SUCCESS ) {
255 new_messages = -1;
256 goto end;
259 argv[0] = IMAP4_CMD_LOGIN;
260 argv[1] = wmnotify_infos.username;
261 argv[2] = wmnotify_infos.password;
262 status = IMAP4_SendCommand( 3, argv );
263 if( status != EXIT_SUCCESS ) {
264 new_messages = -1;
265 goto imap4_logout;
268 /* Selecting the mailbox first. */
269 argv[0] = IMAP4_CMD_EXAMINE;
270 argv[1] = wmnotify_infos.imap_folder;
271 status = IMAP4_SendCommand( 2, argv );
272 if( status != EXIT_SUCCESS ) {
273 new_messages = -1;
274 goto imap4_logout;
277 /* Searching in selected mailbox for new messages. We must use the UNSEEN search criteria
278 * instead of NEW (combination of RECENT and UNSEEN). If there is a new message, RECENT
279 * and UNSEEN will have entries. But if we recheck again later, RECENT will report zero.
280 * RECENT, when set, simply means that there are new messages since our last visit.
281 But, on the other hand, when using EXAMINE, no messages should lose their RECENT flag. */
282 unseen_string_found = false;
283 argv[0] = IMAP4_CMD_SEARCH_UNSEEN;
284 argv[1] = "";
285 status = IMAP4_SendCommand( 1, argv );
286 if( status != EXIT_SUCCESS ) {
287 new_messages = -1;
288 goto imap4_logout;
291 if( unseen_string_found == true ) {
292 new_messages = 1;
295 imap4_logout:
296 argv[0] = IMAP4_CMD_LOGOUT;
297 status = IMAP4_SendCommand( 1, argv );
298 if( status != EXIT_SUCCESS ) {
299 new_messages = -1;
302 status = ConnectionTerminate();
303 if( status != EXIT_SUCCESS ) {
304 new_messages = -1;
307 end:
308 return new_messages;