Tizen 2.0 Release
[external/libgnutls26.git] / lib / opencdk / new-packet.c
1 /* new-packet.c - packet handling (freeing, copying, ...)
2  * Copyright (C) 2001, 2002, 2003, 2007, 2008, 2010 Free Software
3  * Foundation, Inc.
4  *
5  * Author: Timo Schulz
6  *
7  * This file is part of OpenCDK.
8  *
9  * The OpenCDK library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA
23  *
24  */
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 #include <string.h>
29 #include <stdio.h>
30
31 #include "opencdk.h"
32 #include "main.h"
33 #include "packet.h"
34
35
36 /* Release an array of MPI values. */
37 void
38 _cdk_free_mpibuf (size_t n, bigint_t * array)
39 {
40   while (n--)
41     {
42       _gnutls_mpi_release (&array[n]);
43     }
44 }
45
46
47 /**
48  * cdk_pkt_new:
49  * @r_pkt: the new packet
50  * 
51  * Allocate a new packet.
52  **/
53 cdk_error_t
54 cdk_pkt_new (cdk_packet_t * r_pkt)
55 {
56   cdk_packet_t pkt;
57
58   if (!r_pkt)
59     return CDK_Inv_Value;
60   pkt = cdk_calloc (1, sizeof *pkt);
61   if (!pkt)
62     return CDK_Out_Of_Core;
63   *r_pkt = pkt;
64   return 0;
65 }
66
67
68 static void
69 free_pubkey_enc (cdk_pkt_pubkey_enc_t enc)
70 {
71   size_t nenc;
72
73   if (!enc)
74     return;
75
76   nenc = cdk_pk_get_nenc (enc->pubkey_algo);
77   _cdk_free_mpibuf (nenc, enc->mpi);
78   cdk_free (enc);
79 }
80
81
82 static void
83 free_literal (cdk_pkt_literal_t pt)
84 {
85   if (!pt)
86     return;
87   /* The buffer which is referenced in this packet is closed
88      elsewhere. To close it here would cause a double close. */
89   cdk_free (pt);
90 }
91
92
93 void
94 _cdk_free_userid (cdk_pkt_userid_t uid)
95 {
96   if (!uid)
97     return;
98
99   cdk_free (uid->prefs);
100   uid->prefs = NULL;
101   cdk_free (uid->attrib_img);
102   uid->attrib_img = NULL;
103   cdk_free (uid);
104 }
105
106
107 void
108 _cdk_free_signature (cdk_pkt_signature_t sig)
109 {
110   cdk_desig_revoker_t r;
111   size_t nsig;
112
113   if (!sig)
114     return;
115
116   nsig = cdk_pk_get_nsig (sig->pubkey_algo);
117   _cdk_free_mpibuf (nsig, sig->mpi);
118
119   cdk_subpkt_free (sig->hashed);
120   sig->hashed = NULL;
121   cdk_subpkt_free (sig->unhashed);
122   sig->unhashed = NULL;
123   while (sig->revkeys)
124     {
125       r = sig->revkeys->next;
126       cdk_free (sig->revkeys);
127       sig->revkeys = r;
128     }
129   cdk_free (sig);
130 }
131
132
133 void
134 cdk_pk_release (cdk_pubkey_t pk)
135 {
136   size_t npkey;
137
138   if (!pk)
139     return;
140
141   npkey = cdk_pk_get_npkey (pk->pubkey_algo);
142   _cdk_free_userid (pk->uid);
143   pk->uid = NULL;
144   cdk_free (pk->prefs);
145   pk->prefs = NULL;
146   _cdk_free_mpibuf (npkey, pk->mpi);
147   cdk_free (pk);
148 }
149
150
151 void
152 cdk_sk_release (cdk_seckey_t sk)
153 {
154   size_t nskey;
155
156   if (!sk)
157     return;
158
159   nskey = cdk_pk_get_nskey (sk->pubkey_algo);
160   _cdk_free_mpibuf (nskey, sk->mpi);
161   cdk_free (sk->encdata);
162   sk->encdata = NULL;
163   cdk_pk_release (sk->pk);
164   sk->pk = NULL;
165   cdk_s2k_free (sk->protect.s2k);
166   sk->protect.s2k = NULL;
167   cdk_free (sk);
168 }
169
170
171 /* Detach the openpgp packet from the packet structure
172    and release the packet structure itself. */
173 void
174 _cdk_pkt_detach_free (cdk_packet_t pkt, int *r_pkttype, void **ctx)
175 {
176   /* For now we just allow this for keys. */
177   switch (pkt->pkttype)
178     {
179     case CDK_PKT_PUBLIC_KEY:
180     case CDK_PKT_PUBLIC_SUBKEY:
181       *ctx = pkt->pkt.public_key;
182       break;
183
184     case CDK_PKT_SECRET_KEY:
185     case CDK_PKT_SECRET_SUBKEY:
186       *ctx = pkt->pkt.secret_key;
187       break;
188
189     default:
190       *r_pkttype = 0;
191       return;
192     }
193
194   /* The caller might expect a specific packet type and
195      is not interested to store it for later use. */
196   if (r_pkttype)
197     *r_pkttype = pkt->pkttype;
198
199   cdk_free (pkt);
200 }
201
202
203 void
204 cdk_pkt_free (cdk_packet_t pkt)
205 {
206   if (!pkt)
207     return;
208
209   switch (pkt->pkttype)
210     {
211     case CDK_PKT_ATTRIBUTE:
212     case CDK_PKT_USER_ID:
213       _cdk_free_userid (pkt->pkt.user_id);
214       break;
215     case CDK_PKT_PUBLIC_KEY:
216     case CDK_PKT_PUBLIC_SUBKEY:
217       cdk_pk_release (pkt->pkt.public_key);
218       break;
219     case CDK_PKT_SECRET_KEY:
220     case CDK_PKT_SECRET_SUBKEY:
221       cdk_sk_release (pkt->pkt.secret_key);
222       break;
223     case CDK_PKT_SIGNATURE:
224       _cdk_free_signature (pkt->pkt.signature);
225       break;
226     case CDK_PKT_PUBKEY_ENC:
227       free_pubkey_enc (pkt->pkt.pubkey_enc);
228       break;
229     case CDK_PKT_MDC:
230       cdk_free (pkt->pkt.mdc);
231       break;
232     case CDK_PKT_ONEPASS_SIG:
233       cdk_free (pkt->pkt.onepass_sig);
234       break;
235     case CDK_PKT_LITERAL:
236       free_literal (pkt->pkt.literal);
237       break;
238     case CDK_PKT_COMPRESSED:
239       cdk_free (pkt->pkt.compressed);
240       break;
241     default:
242       break;
243     }
244
245   /* Reset the packet type to avoid, when cdk_pkt_release() will be
246      used, that the second cdk_pkt_free() call will double free the data. */
247   pkt->pkttype = 0;
248 }
249
250
251 /**
252  * cdk_pkt_release:
253  * @pkt: the packet
254  * 
255  * Free the contents of the given package and
256  * release the memory of the structure.
257  **/
258 void
259 cdk_pkt_release (cdk_packet_t pkt)
260 {
261   if (!pkt)
262     return;
263   cdk_pkt_free (pkt);
264   cdk_free (pkt);
265 }
266
267
268 /**
269  * cdk_pkt_alloc:
270  * @r_pkt: output is the new packet
271  * @pkttype: the requested packet type
272  * 
273  * Allocate a new packet structure with the given packet type.
274  **/
275 cdk_error_t
276 cdk_pkt_alloc (cdk_packet_t * r_pkt, cdk_packet_type_t pkttype)
277 {
278   cdk_packet_t pkt;
279   int rc;
280
281   if (!r_pkt)
282     return CDK_Inv_Value;
283
284   rc = cdk_pkt_new (&pkt);
285   if (rc)
286     return rc;
287
288   switch (pkttype)
289     {
290     case CDK_PKT_USER_ID:
291       pkt->pkt.user_id = cdk_calloc (1, sizeof pkt->pkt.user_id);
292       if (!pkt->pkt.user_id)
293         return CDK_Out_Of_Core;
294       pkt->pkt.user_id->name = NULL;
295       break;
296
297     case CDK_PKT_PUBLIC_KEY:
298     case CDK_PKT_PUBLIC_SUBKEY:
299       pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
300       if (!pkt->pkt.public_key)
301         return CDK_Out_Of_Core;
302       break;
303
304     case CDK_PKT_SECRET_KEY:
305     case CDK_PKT_SECRET_SUBKEY:
306       pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
307       pkt->pkt.secret_key->pk =
308         cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk);
309       if (!pkt->pkt.secret_key || !pkt->pkt.secret_key->pk)
310         return CDK_Out_Of_Core;
311       break;
312
313     case CDK_PKT_SIGNATURE:
314       pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature);
315       if (!pkt->pkt.signature)
316         return CDK_Out_Of_Core;
317       break;
318
319     case CDK_PKT_PUBKEY_ENC:
320       pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc);
321       if (!pkt->pkt.pubkey_enc)
322         return CDK_Out_Of_Core;
323       break;
324
325     case CDK_PKT_MDC:
326       pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc);
327       if (!pkt->pkt.mdc)
328         return CDK_Out_Of_Core;
329       break;
330
331     case CDK_PKT_ONEPASS_SIG:
332       pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig);
333       if (!pkt->pkt.onepass_sig)
334         return CDK_Out_Of_Core;
335       break;
336
337     case CDK_PKT_LITERAL:
338       /* FIXME: We would need the size of the file name to allocate extra
339          bytes, otherwise the result would be useless. */
340       pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal);
341       if (!pkt->pkt.literal)
342         return CDK_Out_Of_Core;
343       pkt->pkt.literal->name = NULL;
344       break;
345
346     default:
347       return CDK_Not_Implemented;
348     }
349   pkt->pkttype = pkttype;
350   *r_pkt = pkt;
351   return 0;
352 }
353
354
355 cdk_prefitem_t
356 _cdk_copy_prefs (const cdk_prefitem_t prefs)
357 {
358   size_t n = 0;
359   struct cdk_prefitem_s *new_prefs;
360
361   if (!prefs)
362     return NULL;
363
364   for (n = 0; prefs[n].type; n++)
365     ;
366   new_prefs = cdk_calloc (1, sizeof *new_prefs * (n + 1));
367   if (!new_prefs)
368     return NULL;
369   for (n = 0; prefs[n].type; n++)
370     {
371       new_prefs[n].type = prefs[n].type;
372       new_prefs[n].value = prefs[n].value;
373     }
374   new_prefs[n].type = CDK_PREFTYPE_NONE;
375   new_prefs[n].value = 0;
376   return new_prefs;
377 }
378
379
380 cdk_error_t
381 _cdk_copy_userid (cdk_pkt_userid_t * dst, cdk_pkt_userid_t src)
382 {
383   cdk_pkt_userid_t u;
384
385   if (!dst || !src)
386     return CDK_Inv_Value;
387
388   *dst = NULL;
389   u = cdk_calloc (1, sizeof *u + strlen (src->name) + 2);
390   if (!u)
391     return CDK_Out_Of_Core;
392   u->name = (char *) u + sizeof (*u);
393
394   memcpy (u, src, sizeof *u);
395   memcpy (u->name, src->name, strlen (src->name));
396   u->prefs = _cdk_copy_prefs (src->prefs);
397   if (src->selfsig)
398     _cdk_copy_signature (&u->selfsig, src->selfsig);
399   *dst = u;
400
401   return 0;
402 }
403
404
405 cdk_error_t
406 _cdk_copy_pubkey (cdk_pkt_pubkey_t * dst, cdk_pkt_pubkey_t src)
407 {
408   cdk_pkt_pubkey_t k;
409   int i;
410
411   if (!dst || !src)
412     return CDK_Inv_Value;
413
414   *dst = NULL;
415   k = cdk_calloc (1, sizeof *k);
416   if (!k)
417     return CDK_Out_Of_Core;
418   memcpy (k, src, sizeof *k);
419   if (src->uid)
420     _cdk_copy_userid (&k->uid, src->uid);
421   if (src->prefs)
422     k->prefs = _cdk_copy_prefs (src->prefs);
423   for (i = 0; i < cdk_pk_get_npkey (src->pubkey_algo); i++)
424     k->mpi[i] = _gnutls_mpi_copy (src->mpi[i]);
425   *dst = k;
426
427   return 0;
428 }
429
430
431 cdk_error_t
432 _cdk_copy_seckey (cdk_pkt_seckey_t * dst, cdk_pkt_seckey_t src)
433 {
434   cdk_pkt_seckey_t k;
435   int i;
436
437   if (!dst || !src)
438     return CDK_Inv_Value;
439
440   *dst = NULL;
441   k = cdk_calloc (1, sizeof *k);
442   if (!k)
443     return CDK_Out_Of_Core;
444   memcpy (k, src, sizeof *k);
445   _cdk_copy_pubkey (&k->pk, src->pk);
446
447   if (src->encdata)
448     {
449       k->encdata = cdk_calloc (1, src->enclen + 1);
450       if (!k->encdata)
451         return CDK_Out_Of_Core;
452       memcpy (k->encdata, src->encdata, src->enclen);
453     }
454
455   _cdk_s2k_copy (&k->protect.s2k, src->protect.s2k);
456   for (i = 0; i < cdk_pk_get_nskey (src->pubkey_algo); i++)
457     {
458       k->mpi[i] = _gnutls_mpi_copy (src->mpi[i]);
459     }
460
461   *dst = k;
462   return 0;
463 }
464
465
466 cdk_error_t
467 _cdk_copy_pk_to_sk (cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk)
468 {
469   if (!pk || !sk)
470     return CDK_Inv_Value;
471
472   sk->version = pk->version;
473   sk->expiredate = pk->expiredate;
474   sk->pubkey_algo = _pgp_pub_algo_to_cdk (pk->pubkey_algo);
475   sk->has_expired = pk->has_expired;
476   sk->is_revoked = pk->is_revoked;
477   sk->main_keyid[0] = pk->main_keyid[0];
478   sk->main_keyid[1] = pk->main_keyid[1];
479   sk->keyid[0] = pk->keyid[0];
480   sk->keyid[1] = pk->keyid[1];
481
482   return 0;
483 }
484
485
486 cdk_error_t
487 _cdk_copy_signature (cdk_pkt_signature_t * dst, cdk_pkt_signature_t src)
488 {
489   cdk_pkt_signature_t s;
490
491   if (!dst || !src)
492     return CDK_Inv_Value;
493
494   *dst = NULL;
495   s = cdk_calloc (1, sizeof *s);
496   if (!s)
497     return CDK_Out_Of_Core;
498   memcpy (s, src, sizeof *src);
499   _cdk_subpkt_copy (&s->hashed, src->hashed);
500   _cdk_subpkt_copy (&s->unhashed, src->unhashed);
501   /* FIXME: Copy MPI parts */
502   *dst = s;
503
504   return 0;
505 }
506
507
508 cdk_error_t
509 _cdk_pubkey_compare (cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b)
510 {
511   int na, nb, i;
512
513   if (a->timestamp != b->timestamp || a->pubkey_algo != b->pubkey_algo)
514     return -1;
515   if (a->version < 4 && a->expiredate != b->expiredate)
516     return -1;
517   na = cdk_pk_get_npkey (a->pubkey_algo);
518   nb = cdk_pk_get_npkey (b->pubkey_algo);
519   if (na != nb)
520     return -1;
521
522   for (i = 0; i < na; i++)
523     {
524       if (_gnutls_mpi_cmp (a->mpi[i], b->mpi[i]))
525         return -1;
526     }
527
528   return 0;
529 }
530
531
532 /**
533  * cdk_subpkt_free:
534  * @ctx: the sub packet node to free
535  *
536  * Release the context.
537  **/
538 void
539 cdk_subpkt_free (cdk_subpkt_t ctx)
540 {
541   cdk_subpkt_t s;
542
543   while (ctx)
544     {
545       s = ctx->next;
546       cdk_free (ctx);
547       ctx = s;
548     }
549 }
550
551
552 /**
553  * cdk_subpkt_find:
554  * @ctx: the sub packet node
555  * @type: the packet type to find
556  *
557  * Find the given packet type in the node. If no packet with this
558  * type was found, return null otherwise pointer to the node.
559  **/
560 cdk_subpkt_t
561 cdk_subpkt_find (cdk_subpkt_t ctx, size_t type)
562 {
563   return cdk_subpkt_find_nth (ctx, type, 0);
564 }
565
566 /**
567  * cdk_subpkt_type_count:
568  * @ctx: The sub packet context
569  * @type: The sub packet type.
570  * 
571  * Return the amount of sub packets with this type.
572  **/
573 size_t
574 cdk_subpkt_type_count (cdk_subpkt_t ctx, size_t type)
575 {
576   cdk_subpkt_t s;
577   size_t count;
578
579   count = 0;
580   for (s = ctx; s; s = s->next)
581     {
582       if (s->type == type)
583         count++;
584     }
585
586   return count;
587 }
588
589
590 /**
591  * cdk_subpkt_find_nth:
592  * @ctx: The sub packet context
593  * @type: The sub packet type
594  * @index: The nth packet to retrieve, 0 means the first
595  * 
596  * Return the nth sub packet of the given type.
597  **/
598 cdk_subpkt_t
599 cdk_subpkt_find_nth (cdk_subpkt_t ctx, size_t type, size_t idx)
600 {
601   cdk_subpkt_t s;
602   size_t pos;
603
604   pos = 0;
605   for (s = ctx; s; s = s->next)
606     {
607       if (s->type == type && pos++ == idx)
608         return s;
609     }
610
611   return NULL;
612 }
613
614
615 /**
616  * cdk_subpkt_new:
617  * @size: the size of the new context
618  *
619  * Create a new sub packet node with the size of @size.
620  **/
621 cdk_subpkt_t
622 cdk_subpkt_new (size_t size)
623 {
624   cdk_subpkt_t s;
625
626   if (!size)
627     return NULL;
628   s = cdk_calloc (1, sizeof *s + size + 2);
629   if (!s)
630     return NULL;
631   s->d = (char *) s + sizeof (*s);
632
633   return s;
634 }
635
636
637 /**
638  * cdk_subpkt_get_data:
639  * @ctx: the sub packet node
640  * @r_type: pointer store the packet type
641  * @r_nbytes: pointer to store the packet size
642  *
643  * Extract the data from the given sub packet. The type is returned
644  * in @r_type and the size in @r_nbytes.
645  **/
646 const byte *
647 cdk_subpkt_get_data (cdk_subpkt_t ctx, size_t * r_type, size_t * r_nbytes)
648 {
649   if (!ctx || !r_nbytes)
650     return NULL;
651   if (r_type)
652     *r_type = ctx->type;
653   *r_nbytes = ctx->size;
654   return ctx->d;
655 }
656
657
658 /**
659  * cdk_subpkt_add:
660  * @root: the root node
661  * @node: the node to add
662  *
663  * Add the node in @node to the root node @root.
664  **/
665 cdk_error_t
666 cdk_subpkt_add (cdk_subpkt_t root, cdk_subpkt_t node)
667 {
668   cdk_subpkt_t n1;
669
670   if (!root)
671     return CDK_Inv_Value;
672   for (n1 = root; n1->next; n1 = n1->next)
673     ;
674   n1->next = node;
675   return 0;
676 }
677
678
679 byte *
680 _cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes)
681 {
682   cdk_subpkt_t list;
683   byte *buf;
684   size_t n, nbytes;
685
686   if (!s)
687     {
688       if (r_nbytes)
689         *r_nbytes = 0;
690       return NULL;
691     }
692
693   for (n = 0, list = s; list; list = list->next)
694     {
695       n++;                      /* type */
696       n += list->size;
697       if (list->size < 192)
698         n++;
699       else if (list->size < 8384)
700         n += 2;
701       else
702         n += 5;
703     }
704   buf = cdk_calloc (1, n + 1);
705   if (!buf)
706     return NULL;
707
708   n = 0;
709   for (list = s; list; list = list->next)
710     {
711       nbytes = 1 + list->size;  /* type */
712       if (nbytes < 192)
713         buf[n++] = nbytes;
714       else if (nbytes < 8384)
715         {
716           buf[n++] = nbytes / 256 + 192;
717           buf[n++] = nbytes % 256;
718         }
719       else
720         {
721           buf[n++] = 0xFF;
722           buf[n++] = nbytes >> 24;
723           buf[n++] = nbytes >> 16;
724           buf[n++] = nbytes >> 8;
725           buf[n++] = nbytes;
726         }
727       buf[n++] = list->type;
728       memcpy (buf + n, list->d, list->size);
729       n += list->size;
730     }
731
732   if (count)
733     {
734       cdk_free (buf);
735       buf = NULL;
736     }
737   if (r_nbytes)
738     *r_nbytes = n;
739   return buf;
740 }
741
742
743 cdk_error_t
744 _cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src)
745 {
746   cdk_subpkt_t root, p, node;
747
748   if (!src || !r_dst)
749     return CDK_Inv_Value;
750
751   root = NULL;
752   for (p = src; p; p = p->next)
753     {
754       node = cdk_subpkt_new (p->size);
755       if (node)
756         {
757           memcpy (node->d, p->d, p->size);
758           node->type = p->type;
759           node->size = p->size;
760         }
761       if (!root)
762         root = node;
763       else
764         cdk_subpkt_add (root, node);
765     }
766   *r_dst = root;
767   return 0;
768 }
769
770
771 /**
772  * cdk_subpkt_init:
773  * @node: the sub packet node
774  * @type: type of the packet which data should be initialized
775  * @buf: the buffer with the actual data
776  * @buflen: the size of the data
777  *
778  * Set the packet data of the given root and set the type of it.
779  **/
780 void
781 cdk_subpkt_init (cdk_subpkt_t node, size_t type,
782                  const void *buf, size_t buflen)
783 {
784   if (!node)
785     return;
786   node->type = type;
787   node->size = buflen;
788   memcpy (node->d, buf, buflen);
789 }
790
791
792 /* FIXME: We need to think of a public interface for it. */
793 const byte *
794 cdk_key_desig_revoker_walk (cdk_desig_revoker_t root,
795                             cdk_desig_revoker_t * ctx,
796                             int *r_class, int *r_algid)
797 {
798   cdk_desig_revoker_t n;
799
800   if (!*ctx)
801     {
802       *ctx = root;
803       n = root;
804     }
805   else
806     {
807       n = (*ctx)->next;
808       *ctx = n;
809     }
810
811   if (n && r_class && r_algid)
812     {
813       *r_class = n->r_class;
814       *r_algid = n->algid;
815     }
816
817   return n ? n->fpr : NULL;
818 }
819
820
821 /**
822  * cdk_subpkt_find_next:
823  * @root: the base where to begin the iteration
824  * @type: the type to find or 0 for the next node.
825  * 
826  * Try to find the next node after @root with type.
827  * If type is 0, the next node will be returned.
828  **/
829 cdk_subpkt_t
830 cdk_subpkt_find_next (cdk_subpkt_t root, size_t type)
831 {
832   cdk_subpkt_t node;
833
834   for (node = root->next; node; node = node->next)
835     {
836       if (!type)
837         return node;
838       else if (node->type == type)
839         return node;
840     }
841
842   return NULL;
843 }