x400_mttutorial.c
1 /* Copyright (c) 2008-2010, Isode Limited, London, England.
2  * All rights reserved.
3  *
4  * Acquisition and use of this software and related materials for any
5  * purpose requires a written licence agreement from Isode Limited,
6  * or a written licence from an organisation licenced by Isode Limited
7  * to grant such a licence.
8  *
9  */
10 
11 /* x400_mttutorial.c
12  *
13  * The purpose of this file is to simply explain how to send and receive
14  * X400 email messages using the Message Transfer (Gateway) API.
15  *
16  */
17 
18 /*
19  * Setting up your configuration.
20  * Before you go any further you need to have a suitable configuration created
21  *
22  * The purpose of this tutorial is to provide a very simple example of the
23  * basic principles of the Isode X.400api
24  *
25  * Specifically it demonstrates:
26  * 1) The Object Oriented approach to manipulating X.400 Messages
27  * 2) How to create a new "Session"
28  * 3) How to create a new message associated with that session.
29  * 4) How to add attributes to that Message.
30  * 5) How to receive a Message.
31  * 6) How to retrieve attributes from that Message.
32  * 7) How to close a session.
33  * 8) How to safely destroy a Message.
34  *
35  * This tutorial will only cover these basics.
36  * the example X.400api programs give a more detailed example of how to use
37  * the X.400api.
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 
43 
44 #include <x400_mtapi.h>
45 #include "example.h"
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <time.h>
51 
52 /* Define IC_ATTRIBUTE
53  * For gcc/g++ this becomes __attribute__ and is used for the printf type
54  * format checking.
55  * Defined to be empty for other compilers
56  */
57 
58 #if defined(__GNUC__) || defined (__GNUG__)
59 #define IC_ATTRIBUTE(x) __attribute__ (x)
60 #else
61 #define IC_ATTRIBUTE(x)
62 #endif
63 
64 /* To annotate function parameters which are known not to be used
65  * Not in gcc/g++ prior to v4 (I think)
66  */
67 #if (defined(__GNUG__) && __GNUG__ >= 4) || (!defined(__GNUG__) && defined(__GNUC__) && __GNUC__ >= 3)
68 # define ARGNOTUSED IC_ATTRIBUTE((unused))
69 #else
70 # define ARGNOTUSED
71 #endif
72 
73 char *orig = "/CN=GatewayUser/OU=Sales/OU=dhcp-164/O=GatewayMTA/PRMD=TestPRMD/ADMD=TestADMD/C=GB/";
74 char *recip = "/CN=GatewayUser/OU=Sales/OU=dhcp-164/O=GatewayMTA/PRMD=TestPRMD/ADMD=TestADMD/C=GB/";
75 
76 static void send_hello_world(
77  struct X400mtSession *sp
78 );
79 
80 static void receive_hello_world(
81  struct X400mtSession *sp
82 );
83 
84 int main (int argc ARGNOTUSED, char ** argv ARGNOTUSED)
85 {
86  int status;
87  struct X400mtSession *sp; /* This is the session pointer object.
88  * All susequent objects like message objects
89  * are associated back to this session
90  * When the session is closed these objects are
91  * free'd.
92  */
93 
94  /* Open a new session, and check that the session has been opened ok.
95  * The different values the API can return are in x400_att.h */
96 
97  status = X400mtOpen (x400_channel, &sp);
98  if ( status != X400_E_NOERROR ) {
99  fprintf (stderr, "Error in Open: %s\n", X400mtError (status));
100  exit (status);
101  }
102 
103  /* We now want to configure logging
104  * Isode logging is configured using a specific XML file.
105  * a GUI editor SBINDIR/logconfig allows you to easily alter the XML file.
106  *
107  * To specify a particular xml file, we need to manipulate the
108  * session object.
109  *
110  * To do this we call X400mtSetStrDefault, passing in the session
111  * pointer, the attribute of the object we wish to manipulate
112  * (X400_S_LOG_CONFIGURATION_FILE), the filename, and the size in bytes
113  * of the filename.
114  *
115  * Since we don't actually have the size of the filename in bytes,
116  * we can pass in 0 or -1 in for the size field. The underlying code will
117  * calculate the size for us.
118  */
119 
120  X400mtSetStrDefault(sp, X400_S_LOG_CONFIGURATION_FILE, "x400api.xml", 0);
121 
122  /* The reference section of the API manual contains descriptions of
123  * other default attributes you may set.
124  */
125 
126  /* We can now attempt to send a message */
127  send_hello_world(sp);
128 
129 
130 
131  /* We can now attempt to receive a message */
132  receive_hello_world(sp);
133 
134  X400mtClose (sp);
135  if ( status != X400_E_NOERROR ) {
136  fprintf (stderr, "Error in Close: %s\n", X400mtError (status));
137  exit (status);
138  }
139 
140  return 0;
141 }
142 
143 
144 /*
145  * 1) Sending a "Hello World!" email
146  *
147  * You will need to know a single X.400 email addresses.
148  * This email address needs to be routed to use the Gateway channel.
149  *
150  * Fortunately quickconfig helps by providing a suitable user:
151  * /CN=GatewayUser/OU=Sales/OU=HOSTNAME/O=GatewayMTA/PRMD=TestPRMD/ADMD=TestADMD/C=GB/
152  *
153  * You should alter "HOSTNAME" to being the correct value for your
154  * configuration.
155  *
156  * The following section will show you how to send a "Hello World!"
157  * X.400 email througth the messag transfer API.
158  */
159 
160 static void send_hello_world(
161  struct X400mtSession *sp
162 )
163 {
164  int status;
165 
166  /* Now lets create a new message */
167  struct X400mtMessage *mp; /* This is the pointer to a message object */
168  struct X400Recipient *rp; /* This is the pointer to a new recipient */
169 
170 
171  printf("Now sending simple message\n");
172  /* We now need to create a new message object, and have the new
173  * message object associated with the session object.
174  *
175  * To do this we use X400mtMsgNew.
176  */
177  status = X400mtMsgNew (sp, X400_MSG_MESSAGE, &mp);
178 
179  if ( status != X400_E_NOERROR ) {
180  fprintf (stderr, "Error in MsgNew: %s\n", X400mtError (status));
181  exit (status);
182  }
183 
184  /* We can now manipulate the message object */
185 
186  /* Add an originator. Similar concepts here,
187  * we are manipulating the message object to add an originator address
188  */
189  status = X400mtMsgAddStrParam (mp, X400_S_OR_ADDRESS, orig, -1);
190 
191  if ( status != X400_E_NOERROR ) {
192  fprintf (stderr, "Error adding orignator: %s\n", X400mtError (status));
193  exit (status);
194  }
195 
196  /* Create a new recipient object, and associate it with the message object.
197  */
198  status = X400mtRecipNew (mp, X400_RECIP_STANDARD, &rp);
199  if ( status != X400_E_NOERROR ) {
200  fprintf (stderr, "Error adding recipient: %s\n", X400mtError (status));
201  exit (status);
202  }
203 
204  /* We can now manipulate the recipient object and add the following:
205  * 1) The OR address of the recipient.
206  * Obviously this is important, otherwise the message cannot be routed.
207  *
208  * 2) The responsibility value
209  * You don't actually need to set this. However it is usefull to see
210  * how the attributes being manipulated correspond with the standards.
211  */
212  status = X400mtRecipAddStrParam (rp, X400_S_OR_ADDRESS, recip, -1);
213  if ( status != X400_E_NOERROR ) {
214  fprintf (stderr, "Error adding recipient address: %s\n",
215  X400mtError (status));
216  exit (status);
217  }
218 
219  /* The following attribute might see slightly confusing.
220  * The best thing to do at this point is to take a brief look
221  * at X.411, section 12.2.1.1.1.6, this describes the responsibility
222  * attribute.
223  */
224 
226  if ( status != X400_E_NOERROR ) {
227  fprintf (stderr, "Error adding recipient responsibility: %s\n",
228  X400mtError (status));
229  exit (status);
230  }
231 
232  /* Extra envelope attributes */
233  status = X400mtMsgAddIntParam (mp, X400_N_CONTENT_TYPE, 2);
234  if ( status != X400_E_NOERROR ) {
235  fprintf (stderr, "Error adding Content type : %s\n",
236  X400mtError (status));
237  exit (status);
238  }
239 
240  /* And now for the content */
241  {
242  char *content = "Hello World!";
243  char *subj = "A simple test message";
244 
245  status = X400mtMsgAddStrParam (mp,X400_T_IA5TEXT, content , -1);
246  if ( status != X400_E_NOERROR ) {
247  fprintf (stderr, "Error adding content : %s\n",
248  X400mtError (status));
249  exit (status);
250  }
251 
252  status = X400mtMsgAddStrParam (mp, X400_S_SUBJECT, subj, -1);
253  if ( status != X400_E_NOERROR ) {
254  fprintf (stderr, "Error adding subject : %s\n",
255  X400mtError (status));
256  exit (status);
257  }
258  }
259 
260  /* we should now be able to send the message */
261  status = X400mtMsgSend (mp);
262  if ( status != X400_E_NOERROR ) {
263  fprintf (stderr, "Error in MsgSend: %s\n", X400mtError (status));
264  exit (status);
265  }
266 
267  /* We can now delete the message */
268  status = X400mtMsgDelete (mp);
269  if ( status != X400_E_NOERROR ) {
270  fprintf (stderr, "Error in X400mtMsgDelete: %s\n", X400mtError (status));
271  exit (status);
272  }
273 
274  printf("Sent message\n");
275 }
276 
277 
278 static void receive_hello_world(
279  struct X400mtSession *sp
280 )
281 {
282  struct X400mtMessage *mp;
283  int status;
284  int type;
285  char buffer[BUFSIZ];
286  size_t length;
287 
288  printf("Now fetching message\n");
289 
290  status = X400mtMsgGetStart (sp, &mp, &type);
291  if ( status != X400_E_NOERROR ) {
292  fprintf (stderr, "Error in X400mtMsgGetStart: %s\n",
293  X400mtError (status));
294  exit (status);
295  }
296 
297  switch ( type) {
298  case X400_MSG_REPORT:
299  printf("Got a report\n");
300  exit(0);
301  case X400_MSG_PROBE:
302  printf("Got a probe\n");
303  exit(0);
304  case X400_MSG_MESSAGE:
305  break;
306  }
307 
308  /* Get the originator */
310  buffer, sizeof buffer , &length);
311  if(status == X400_E_NOERROR) {
312  printf ("Originator: %.*s\n",(int)length,buffer);
313  } else if (status == X400_E_NOSPACE) {
314  /* This is an interesting error.
315  * It means that the size of buffer is too small for the originator
316  * address. The correct thing to do here is to create a buffer
317  * of size length bytes to hold the value.
318  *
319  * All of the GetStrParam functions work in this way.
320  */
321 
322  char * big_buff = NULL;
323  big_buff = (char *) malloc((sizeof(char)) * length);
325  big_buff, length , &length);
326  if (status != X400_E_NOERROR) {
327  fprintf(stderr, "Error in getting originator address: %s\n",
328  X400mtError (status));
329  exit (status);
330  }
331  printf("Large Originator: %.*s\n",(int)length,big_buff);
332  free(big_buff);
333 
334  } else {
335  fprintf (stderr, "Error in getting originator address: %s\n",
336  X400mtError (status));
337  exit (status);
338  }
339 
340  {
341  /* Fetching recipients
342  *
343  * Within the message there are 1 or more recipients.
344  * so it is important to try to read through the whole list.
345  *
346  * All list objects work in this same way.
347  */
348  int n;
349  struct X400Recipient *rp; /* Pointer to recipient object */
350  for ( n = 1; ; n++ ) {
351  /* Get the next recipient object with a "Get" command
352  * Notice that I'm using the "X400_RECIP_ENVELOPE".
353  * IE this is fetching recipients from the envelope.
354  * We can fetch recipients from the message header as well
355  * Using X400_RECIP_PRIMARY / X400_RECIP_CC / ETC
356  */
357  status = X400mtRecipGet (mp, X400_RECIP_ENVELOPE, n, &rp);
358  if ( status == X400_E_NO_RECIP ) {
359  printf("Got final recipient\n");
360  break;
361  } else if ( status != X400_E_NOERROR ) {
362  fprintf (stderr, "Error fetching recipients: %s\n",
363  X400mtError (status));
364  exit(status);
365  }
366 
367  /* Now we have a recipient object that represents a single
368  recipient.
369  */
370 
372  buffer, BUFSIZ, &length);
373  if ( status == X400_E_NOERROR ) {
374  printf ("%s recipient %d: %.*s\n", buffer, n,
375  (int)length, buffer);
376  } else {
377  fprintf (stderr, "Error fetching OR Address: %s\n",
378  X400mtError (status));
379  exit (status);
380  }
381 
382  /* It is possible to continue to try to obtain other recipient
383  information. However lets keep it simple here
384  */
385 
386  }
387  }/* end of fetch recips*/
388 
389  /* Now lets obtain the subject of the message */
390  /* Subject */
391  status = X400mtMsgGetStrParam (mp, X400_S_SUBJECT,
392  buffer, sizeof buffer , &length);
393  if ( status == X400_E_NOERROR ) {
394  printf ("Subject: %.*s\n", (int)length, buffer);
395  } else {
396  fprintf (stderr, "Error fetching subject: %s\n",
397  X400mtError (status));
398  exit (status);
399  }
400 
401  /* Followed by the message content (Hello World).
402  * The message we just created contained an ia5-text bodypart
403  * so lets request that
404  * NB this (unwisely) assumes that the first attachment
405  * is an IA5 attachment
406  */
407  status = X400mtMsgGetStrParam (mp, X400_T_IA5TEXT,
408  buffer, sizeof buffer , &length);
409  if ( status == X400_E_NOERROR ) {
410  printf ("Text:\n%.*s\n", (int)length, buffer);
411  } else {
412  fprintf (stderr, "Error fetching ia5-text bodypart: %s\n",
413  X400mtError (status));
414  }
415 
416  /* Now that we've got the message we need to let the MTA know the message
417  * has been transfered ok*/
418  status = X400mtMsgGetFinish (mp, X400_E_NOERROR, -1, -1, "");
419  if ( status != X400_E_NOERROR ) {
420  printf("X400mtMsgFinish returned error %d\n", status);
421  fprintf (stderr, "Error in X400mtMsgFinish: %s\n",
422  X400mtError (status));
423  }
424 
425  /* And now we can delete our own copy of the message */
426  status = X400mtMsgDelete (mp);
427  if ( status != X400_E_NOERROR ) {
428  printf("X400mtMsgDelete returned error %d\n", status);
429  fprintf (stderr, "Error in X400mtMsgDelete: %s\n",
430  X400mtError (status));
431  }
432 
433 }
434 
#define X400_MSG_MESSAGE
Definition: x400_att.h:29
#define X400_S_LOG_CONFIGURATION_FILE
Definition: x400_att.h:1091
#define X400_MSG_PROBE
Definition: x400_att.h:35
int X400mtOpen(const char *credentials, struct X400mtSession **spp)
Open a session to the MTA.
int X400mtMsgNew(struct X400mtSession *sp, int type, struct X400mtMessage **mpp)
Creates new message.
#define X400_N_RESPONSIBILITY
Definition: x400_att.h:642
int X400mtMsgGetStart(struct X400mtSession *sp, struct X400mtMessage **mpp, int *typep)
Get message object for transfer out from MTA.
int X400mtMsgGetFinish(struct X400mtMessage *mp, int status, int reason, int diag, const char *info)
Finish transfer-out of message from MTA, generate DR if required.
X.400 Gateway Interface.
int X400mtMsgGetStrParam(struct X400mtMessage *mp, int paramtype, char *buffer, size_t buflen, size_t *paramlenp)
Return a string-valued parameter from the message object.
#define X400_E_NO_RECIP
Definition: x400_att.h:109
int X400mtRecipNew(struct X400mtMessage *mp, int type, struct X400Recipient **rpp)
Add new recipient to a message.
int X400mtSetStrDefault(struct X400mtSession *sp, int paramtype, const char *value, size_t length)
Set a default string parameter value in a session.
#define X400_MSG_REPORT
Definition: x400_att.h:32
int X400mtMsgAddIntParam(struct X400mtMessage *mp, int paramtype, int value)
Add integer-valued parameter to the message.
#define X400_T_IA5TEXT
Definition: x400_att.h:798
#define X400_E_NOERROR
Definition: x400_att.h:46
int X400mtRecipAddStrParam(struct X400Recipient *rp, int paramtype, const char *value, size_t length)
Add string-valued parameter to the message.
int X400mtRecipAddIntParam(struct X400Recipient *rp, int paramtype, int value)
Add integer-valued parameter to the message.
#define X400_S_OR_ADDRESS
Definition: x400_att.h:349
#define X400_N_CONTENT_TYPE
Definition: x400_att.h:408
const char * X400mtError(int error)
Return string for error code.
#define X400_RECIP_STANDARD
Definition: x400_att.h:341
int X400mtMsgSend(struct X400mtMessage *mp)
Send message object to MTA.
int X400mtMsgAddStrParam(struct X400mtMessage *mp, int paramtype, const char *value, size_t length)
Add string-valued parameter to the message.
int X400mtClose(struct X400mtSession *sp)
Close a X400 Session.
#define X400_S_SUBJECT
Definition: x400_att.h:722
#define X400_RECIP_ENVELOPE
Definition: x400_att.h:335
int X400mtRecipGetStrParam(struct X400Recipient *rp, int paramtype, char *buffer, size_t buflen, size_t *paramlenp)
Return a string-valued parameter from the recipient object.
int X400mtRecipGet(struct X400mtMessage *mp, int type, int number, struct X400Recipient **rpp)
Get recipient object from message.
#define X400_E_NOSPACE
Definition: x400_att.h:112
int X400mtMsgDelete(struct X400mtMessage *mp)
Delete message object.