OLD | NEW |
| (Empty) |
1 /* tasn_prn.c */ | |
2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | |
3 * project 2000. | |
4 */ | |
5 /* ==================================================================== | |
6 * Copyright (c) 2000,2005 The OpenSSL Project. All rights reserved. | |
7 * | |
8 * Redistribution and use in source and binary forms, with or without | |
9 * modification, are permitted provided that the following conditions | |
10 * are met: | |
11 * | |
12 * 1. Redistributions of source code must retain the above copyright | |
13 * notice, this list of conditions and the following disclaimer. | |
14 * | |
15 * 2. Redistributions in binary form must reproduce the above copyright | |
16 * notice, this list of conditions and the following disclaimer in | |
17 * the documentation and/or other materials provided with the | |
18 * distribution. | |
19 * | |
20 * 3. All advertising materials mentioning features or use of this | |
21 * software must display the following acknowledgment: | |
22 * "This product includes software developed by the OpenSSL Project | |
23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" | |
24 * | |
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to | |
26 * endorse or promote products derived from this software without | |
27 * prior written permission. For written permission, please contact | |
28 * licensing@OpenSSL.org. | |
29 * | |
30 * 5. Products derived from this software may not be called "OpenSSL" | |
31 * nor may "OpenSSL" appear in their names without prior written | |
32 * permission of the OpenSSL Project. | |
33 * | |
34 * 6. Redistributions of any form whatsoever must retain the following | |
35 * acknowledgment: | |
36 * "This product includes software developed by the OpenSSL Project | |
37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" | |
38 * | |
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY | |
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR | |
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
50 * OF THE POSSIBILITY OF SUCH DAMAGE. | |
51 * ==================================================================== | |
52 * | |
53 * This product includes cryptographic software written by Eric Young | |
54 * (eay@cryptsoft.com). This product includes software written by Tim | |
55 * Hudson (tjh@cryptsoft.com). | |
56 * | |
57 */ | |
58 | |
59 | |
60 #include <stddef.h> | |
61 #include "cryptlib.h" | |
62 #include <openssl/asn1.h> | |
63 #include <openssl/asn1t.h> | |
64 #include <openssl/objects.h> | |
65 #include <openssl/buffer.h> | |
66 #include <openssl/err.h> | |
67 #include <openssl/x509v3.h> | |
68 #include "asn1_locl.h" | |
69 | |
70 /* Print routines. | |
71 */ | |
72 | |
73 /* ASN1_PCTX routines */ | |
74 | |
75 ASN1_PCTX default_pctx = | |
76 { | |
77 ASN1_PCTX_FLAGS_SHOW_ABSENT, /* flags */ | |
78 0, /* nm_flags */ | |
79 0, /* cert_flags */ | |
80 0, /* oid_flags */ | |
81 0 /* str_flags */ | |
82 }; | |
83 | |
84 | |
85 ASN1_PCTX *ASN1_PCTX_new(void) | |
86 { | |
87 ASN1_PCTX *ret; | |
88 ret = OPENSSL_malloc(sizeof(ASN1_PCTX)); | |
89 if (ret == NULL) | |
90 { | |
91 ASN1err(ASN1_F_ASN1_PCTX_NEW, ERR_R_MALLOC_FAILURE); | |
92 return NULL; | |
93 } | |
94 ret->flags = 0; | |
95 ret->nm_flags = 0; | |
96 ret->cert_flags = 0; | |
97 ret->oid_flags = 0; | |
98 ret->str_flags = 0; | |
99 return ret; | |
100 } | |
101 | |
102 void ASN1_PCTX_free(ASN1_PCTX *p) | |
103 { | |
104 OPENSSL_free(p); | |
105 } | |
106 | |
107 unsigned long ASN1_PCTX_get_flags(ASN1_PCTX *p) | |
108 { | |
109 return p->flags; | |
110 } | |
111 | |
112 void ASN1_PCTX_set_flags(ASN1_PCTX *p, unsigned long flags) | |
113 { | |
114 p->flags = flags; | |
115 } | |
116 | |
117 unsigned long ASN1_PCTX_get_nm_flags(ASN1_PCTX *p) | |
118 { | |
119 return p->nm_flags; | |
120 } | |
121 | |
122 void ASN1_PCTX_set_nm_flags(ASN1_PCTX *p, unsigned long flags) | |
123 { | |
124 p->nm_flags = flags; | |
125 } | |
126 | |
127 unsigned long ASN1_PCTX_get_cert_flags(ASN1_PCTX *p) | |
128 { | |
129 return p->cert_flags; | |
130 } | |
131 | |
132 void ASN1_PCTX_set_cert_flags(ASN1_PCTX *p, unsigned long flags) | |
133 { | |
134 p->cert_flags = flags; | |
135 } | |
136 | |
137 unsigned long ASN1_PCTX_get_oid_flags(ASN1_PCTX *p) | |
138 { | |
139 return p->oid_flags; | |
140 } | |
141 | |
142 void ASN1_PCTX_set_oid_flags(ASN1_PCTX *p, unsigned long flags) | |
143 { | |
144 p->oid_flags = flags; | |
145 } | |
146 | |
147 unsigned long ASN1_PCTX_get_str_flags(ASN1_PCTX *p) | |
148 { | |
149 return p->str_flags; | |
150 } | |
151 | |
152 void ASN1_PCTX_set_str_flags(ASN1_PCTX *p, unsigned long flags) | |
153 { | |
154 p->str_flags = flags; | |
155 } | |
156 | |
157 /* Main print routines */ | |
158 | |
159 static int asn1_item_print_ctx(BIO *out, ASN1_VALUE **fld, int indent, | |
160 const ASN1_ITEM *it, | |
161 const char *fname, const char *sname, | |
162 int nohdr, const ASN1_PCTX *pctx); | |
163 | |
164 int asn1_template_print_ctx(BIO *out, ASN1_VALUE **fld, int indent, | |
165 const ASN1_TEMPLATE *tt, const ASN1_PCTX *pctx); | |
166 | |
167 static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld, | |
168 const ASN1_ITEM *it, int indent, | |
169 const char *fname, const char *sname, | |
170 const ASN1_PCTX *pctx); | |
171 | |
172 static int asn1_print_fsname(BIO *out, int indent, | |
173 const char *fname, const char *sname, | |
174 const ASN1_PCTX *pctx); | |
175 | |
176 int ASN1_item_print(BIO *out, ASN1_VALUE *ifld, int indent, | |
177 const ASN1_ITEM *it, const ASN1_PCTX *pctx) | |
178 { | |
179 const char *sname; | |
180 if (pctx == NULL) | |
181 pctx = &default_pctx; | |
182 if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME) | |
183 sname = NULL; | |
184 else | |
185 sname = it->sname; | |
186 return asn1_item_print_ctx(out, &ifld, indent, it, | |
187 NULL, sname, 0, pctx); | |
188 } | |
189 | |
190 static int asn1_item_print_ctx(BIO *out, ASN1_VALUE **fld, int indent, | |
191 const ASN1_ITEM *it, | |
192 const char *fname, const char *sname, | |
193 int nohdr, const ASN1_PCTX *pctx) | |
194 { | |
195 const ASN1_TEMPLATE *tt; | |
196 const ASN1_EXTERN_FUNCS *ef; | |
197 ASN1_VALUE **tmpfld; | |
198 const ASN1_AUX *aux = it->funcs; | |
199 ASN1_aux_cb *asn1_cb; | |
200 ASN1_PRINT_ARG parg; | |
201 int i; | |
202 if (aux && aux->asn1_cb) | |
203 { | |
204 parg.out = out; | |
205 parg.indent = indent; | |
206 parg.pctx = pctx; | |
207 asn1_cb = aux->asn1_cb; | |
208 } | |
209 else asn1_cb = 0; | |
210 | |
211 if(*fld == NULL) | |
212 { | |
213 if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_ABSENT) | |
214 { | |
215 if (!nohdr && !asn1_print_fsname(out, indent, | |
216 fname, sname, pctx)) | |
217 return 0; | |
218 if (BIO_puts(out, "<ABSENT>\n") <= 0) | |
219 return 0; | |
220 } | |
221 return 1; | |
222 } | |
223 | |
224 switch(it->itype) | |
225 { | |
226 case ASN1_ITYPE_PRIMITIVE: | |
227 if(it->templates) | |
228 { | |
229 if (!asn1_template_print_ctx(out, fld, indent, | |
230 it->templates, pctx)) | |
231 return 0; | |
232 } | |
233 /* fall thru */ | |
234 case ASN1_ITYPE_MSTRING: | |
235 if (!asn1_primitive_print(out, fld, it, | |
236 indent, fname, sname,pctx)) | |
237 return 0; | |
238 break; | |
239 | |
240 case ASN1_ITYPE_EXTERN: | |
241 if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx
)) | |
242 return 0; | |
243 /* Use new style print routine if possible */ | |
244 ef = it->funcs; | |
245 if (ef && ef->asn1_ex_print) | |
246 { | |
247 i = ef->asn1_ex_print(out, fld, indent, "", pctx); | |
248 if (!i) | |
249 return 0; | |
250 if ((i == 2) && (BIO_puts(out, "\n") <= 0)) | |
251 return 0; | |
252 return 1; | |
253 } | |
254 else if (sname && | |
255 BIO_printf(out, ":EXTERNAL TYPE %s\n", sname) <= 0) | |
256 return 0; | |
257 break; | |
258 | |
259 case ASN1_ITYPE_CHOICE: | |
260 #if 0 | |
261 if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx
)) | |
262 return 0; | |
263 #endif | |
264 /* CHOICE type, get selector */ | |
265 i = asn1_get_choice_selector(fld, it); | |
266 /* This should never happen... */ | |
267 if((i < 0) || (i >= it->tcount)) | |
268 { | |
269 if (BIO_printf(out, | |
270 "ERROR: selector [%d] invalid\n", i) <= 0) | |
271 return 0; | |
272 return 1; | |
273 } | |
274 tt = it->templates + i; | |
275 tmpfld = asn1_get_field_ptr(fld, tt); | |
276 if (!asn1_template_print_ctx(out, tmpfld, indent, tt, pctx)) | |
277 return 0; | |
278 break; | |
279 | |
280 case ASN1_ITYPE_SEQUENCE: | |
281 case ASN1_ITYPE_NDEF_SEQUENCE: | |
282 if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx
)) | |
283 return 0; | |
284 if (fname || sname) | |
285 { | |
286 if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE) | |
287 { | |
288 if (BIO_puts(out, " {\n") <= 0) | |
289 return 0; | |
290 } | |
291 else | |
292 { | |
293 if (BIO_puts(out, "\n") <= 0) | |
294 return 0; | |
295 } | |
296 } | |
297 | |
298 if (asn1_cb) | |
299 { | |
300 i = asn1_cb(ASN1_OP_PRINT_PRE, fld, it, &parg); | |
301 if (i == 0) | |
302 return 0; | |
303 if (i == 2) | |
304 return 1; | |
305 } | |
306 | |
307 /* Print each field entry */ | |
308 for(i = 0, tt = it->templates; i < it->tcount; i++, tt++) | |
309 { | |
310 const ASN1_TEMPLATE *seqtt; | |
311 seqtt = asn1_do_adb(fld, tt, 1); | |
312 tmpfld = asn1_get_field_ptr(fld, seqtt); | |
313 if (!asn1_template_print_ctx(out, tmpfld, | |
314 indent + 2, seqtt, pctx)) | |
315 return 0; | |
316 } | |
317 if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE) | |
318 { | |
319 if (BIO_printf(out, "%*s}\n", indent, "") < 0) | |
320 return 0; | |
321 } | |
322 | |
323 if (asn1_cb) | |
324 { | |
325 i = asn1_cb(ASN1_OP_PRINT_POST, fld, it, &parg); | |
326 if (i == 0) | |
327 return 0; | |
328 } | |
329 break; | |
330 | |
331 default: | |
332 BIO_printf(out, "Unprocessed type %d\n", it->itype); | |
333 return 0; | |
334 } | |
335 | |
336 return 1; | |
337 } | |
338 | |
339 int asn1_template_print_ctx(BIO *out, ASN1_VALUE **fld, int indent, | |
340 const ASN1_TEMPLATE *tt, const ASN1_PCTX *pctx) | |
341 { | |
342 int i, flags; | |
343 const char *sname, *fname; | |
344 flags = tt->flags; | |
345 if(pctx->flags & ASN1_PCTX_FLAGS_SHOW_FIELD_STRUCT_NAME) | |
346 sname = ASN1_ITEM_ptr(tt->item)->sname; | |
347 else | |
348 sname = NULL; | |
349 if(pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME) | |
350 fname = NULL; | |
351 else | |
352 fname = tt->field_name; | |
353 if(flags & ASN1_TFLG_SK_MASK) | |
354 { | |
355 char *tname; | |
356 ASN1_VALUE *skitem; | |
357 STACK_OF(ASN1_VALUE) *stack; | |
358 | |
359 /* SET OF, SEQUENCE OF */ | |
360 if (fname) | |
361 { | |
362 if(pctx->flags & ASN1_PCTX_FLAGS_SHOW_SSOF) | |
363 { | |
364 if(flags & ASN1_TFLG_SET_OF) | |
365 tname = "SET"; | |
366 else | |
367 tname = "SEQUENCE"; | |
368 if (BIO_printf(out, "%*s%s OF %s {\n", | |
369 indent, "", tname, tt->field_name) <= 0) | |
370 return 0; | |
371 } | |
372 else if (BIO_printf(out, "%*s%s:\n", indent, "", | |
373 fname) <= 0) | |
374 return 0; | |
375 } | |
376 stack = (STACK_OF(ASN1_VALUE) *)*fld; | |
377 for(i = 0; i < sk_ASN1_VALUE_num(stack); i++) | |
378 { | |
379 if ((i > 0) && (BIO_puts(out, "\n") <= 0)) | |
380 return 0; | |
381 | |
382 skitem = sk_ASN1_VALUE_value(stack, i); | |
383 if (!asn1_item_print_ctx(out, &skitem, indent + 2, | |
384 ASN1_ITEM_ptr(tt->item), NULL, NULL, 1, pctx)) | |
385 return 0; | |
386 } | |
387 if (!i && BIO_printf(out, "%*s<EMPTY>\n", indent + 2, "") <= 0) | |
388 return 0; | |
389 if(pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE) | |
390 { | |
391 if (BIO_printf(out, "%*s}\n", indent, "") <= 0) | |
392 return 0; | |
393 } | |
394 return 1; | |
395 } | |
396 return asn1_item_print_ctx(out, fld, indent, ASN1_ITEM_ptr(tt->item), | |
397 fname, sname, 0, pctx); | |
398 } | |
399 | |
400 static int asn1_print_fsname(BIO *out, int indent, | |
401 const char *fname, const char *sname, | |
402 const ASN1_PCTX *pctx) | |
403 { | |
404 static char spaces[] = " "; | |
405 const int nspaces = sizeof(spaces) - 1; | |
406 | |
407 #if 0 | |
408 if (!sname && !fname) | |
409 return 1; | |
410 #endif | |
411 | |
412 while (indent > nspaces) | |
413 { | |
414 if (BIO_write(out, spaces, nspaces) != nspaces) | |
415 return 0; | |
416 indent -= nspaces; | |
417 } | |
418 if (BIO_write(out, spaces, indent) != indent) | |
419 return 0; | |
420 if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME) | |
421 sname = NULL; | |
422 if (pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME) | |
423 fname = NULL; | |
424 if (!sname && !fname) | |
425 return 1; | |
426 if (fname) | |
427 { | |
428 if (BIO_puts(out, fname) <= 0) | |
429 return 0; | |
430 } | |
431 if (sname) | |
432 { | |
433 if (fname) | |
434 { | |
435 if (BIO_printf(out, " (%s)", sname) <= 0) | |
436 return 0; | |
437 } | |
438 else | |
439 { | |
440 if (BIO_puts(out, sname) <= 0) | |
441 return 0; | |
442 } | |
443 } | |
444 if (BIO_write(out, ": ", 2) != 2) | |
445 return 0; | |
446 return 1; | |
447 } | |
448 | |
449 static int asn1_print_boolean_ctx(BIO *out, int boolval, | |
450 const ASN1_PCTX *pctx) | |
451 { | |
452 const char *str; | |
453 switch (boolval) | |
454 { | |
455 case -1: | |
456 str = "BOOL ABSENT"; | |
457 break; | |
458 | |
459 case 0: | |
460 str = "FALSE"; | |
461 break; | |
462 | |
463 default: | |
464 str = "TRUE"; | |
465 break; | |
466 | |
467 } | |
468 | |
469 if (BIO_puts(out, str) <= 0) | |
470 return 0; | |
471 return 1; | |
472 | |
473 } | |
474 | |
475 static int asn1_print_integer_ctx(BIO *out, ASN1_INTEGER *str, | |
476 const ASN1_PCTX *pctx) | |
477 { | |
478 char *s; | |
479 int ret = 1; | |
480 s = i2s_ASN1_INTEGER(NULL, str); | |
481 if (BIO_puts(out, s) <= 0) | |
482 ret = 0; | |
483 OPENSSL_free(s); | |
484 return ret; | |
485 } | |
486 | |
487 static int asn1_print_oid_ctx(BIO *out, const ASN1_OBJECT *oid, | |
488 const ASN1_PCTX *pctx) | |
489 { | |
490 char objbuf[80]; | |
491 const char *ln; | |
492 ln = OBJ_nid2ln(OBJ_obj2nid(oid)); | |
493 if(!ln) | |
494 ln = ""; | |
495 OBJ_obj2txt(objbuf, sizeof objbuf, oid, 1); | |
496 if (BIO_printf(out, "%s (%s)", ln, objbuf) <= 0) | |
497 return 0; | |
498 return 1; | |
499 } | |
500 | |
501 static int asn1_print_obstring_ctx(BIO *out, ASN1_STRING *str, int indent, | |
502 const ASN1_PCTX *pctx) | |
503 { | |
504 if (str->type == V_ASN1_BIT_STRING) | |
505 { | |
506 if (BIO_printf(out, " (%ld unused bits)\n", | |
507 str->flags & 0x7) <= 0) | |
508 return 0; | |
509 } | |
510 else if (BIO_puts(out, "\n") <= 0) | |
511 return 0; | |
512 if ((str->length > 0) | |
513 && BIO_dump_indent(out, (char *)str->data, str->length, | |
514 indent + 2) <= 0) | |
515 return 0; | |
516 return 1; | |
517 } | |
518 | |
519 static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld, | |
520 const ASN1_ITEM *it, int indent, | |
521 const char *fname, const char *sname, | |
522 const ASN1_PCTX *pctx) | |
523 { | |
524 long utype; | |
525 ASN1_STRING *str; | |
526 int ret = 1, needlf = 1; | |
527 const char *pname; | |
528 const ASN1_PRIMITIVE_FUNCS *pf; | |
529 pf = it->funcs; | |
530 if (!asn1_print_fsname(out, indent, fname, sname, pctx)) | |
531 return 0; | |
532 if (pf && pf->prim_print) | |
533 return pf->prim_print(out, fld, it, indent, pctx); | |
534 str = (ASN1_STRING *)*fld; | |
535 if (it->itype == ASN1_ITYPE_MSTRING) | |
536 utype = str->type & ~V_ASN1_NEG; | |
537 else | |
538 utype = it->utype; | |
539 if (utype == V_ASN1_ANY) | |
540 { | |
541 ASN1_TYPE *atype = (ASN1_TYPE *)*fld; | |
542 utype = atype->type; | |
543 fld = &atype->value.asn1_value; | |
544 str = (ASN1_STRING *)*fld; | |
545 if (pctx->flags & ASN1_PCTX_FLAGS_NO_ANY_TYPE) | |
546 pname = NULL; | |
547 else | |
548 pname = ASN1_tag2str(utype); | |
549 } | |
550 else | |
551 { | |
552 if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_TYPE) | |
553 pname = ASN1_tag2str(utype); | |
554 else | |
555 pname = NULL; | |
556 } | |
557 | |
558 if (utype == V_ASN1_NULL) | |
559 { | |
560 if (BIO_puts(out, "NULL\n") <= 0) | |
561 return 0; | |
562 return 1; | |
563 } | |
564 | |
565 if (pname) | |
566 { | |
567 if (BIO_puts(out, pname) <= 0) | |
568 return 0; | |
569 if (BIO_puts(out, ":") <= 0) | |
570 return 0; | |
571 } | |
572 | |
573 switch (utype) | |
574 { | |
575 case V_ASN1_BOOLEAN: | |
576 { | |
577 int boolval = *(int *)fld; | |
578 if (boolval == -1) | |
579 boolval = it->size; | |
580 ret = asn1_print_boolean_ctx(out, boolval, pctx); | |
581 } | |
582 break; | |
583 | |
584 case V_ASN1_INTEGER: | |
585 case V_ASN1_ENUMERATED: | |
586 ret = asn1_print_integer_ctx(out, str, pctx); | |
587 break; | |
588 | |
589 case V_ASN1_UTCTIME: | |
590 ret = ASN1_UTCTIME_print(out, str); | |
591 break; | |
592 | |
593 case V_ASN1_GENERALIZEDTIME: | |
594 ret = ASN1_GENERALIZEDTIME_print(out, str); | |
595 break; | |
596 | |
597 case V_ASN1_OBJECT: | |
598 ret = asn1_print_oid_ctx(out, (const ASN1_OBJECT *)*fld, pctx); | |
599 break; | |
600 | |
601 case V_ASN1_OCTET_STRING: | |
602 case V_ASN1_BIT_STRING: | |
603 ret = asn1_print_obstring_ctx(out, str, indent, pctx); | |
604 needlf = 0; | |
605 break; | |
606 | |
607 case V_ASN1_SEQUENCE: | |
608 case V_ASN1_SET: | |
609 case V_ASN1_OTHER: | |
610 if (BIO_puts(out, "\n") <= 0) | |
611 return 0; | |
612 if (ASN1_parse_dump(out, str->data, str->length, | |
613 indent, 0) <= 0) | |
614 ret = 0; | |
615 needlf = 0; | |
616 break; | |
617 | |
618 default: | |
619 ret = ASN1_STRING_print_ex(out, str, pctx->str_flags); | |
620 | |
621 } | |
622 if (!ret) | |
623 return 0; | |
624 if (needlf && BIO_puts(out, "\n") <= 0) | |
625 return 0; | |
626 return 1; | |
627 } | |
OLD | NEW |