
/* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images.

   Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net>

   Provided under GPL version 2 or later.

   This file contains functions which manage the relation between xorriso
   and the libraries: libburn, libisofs, libisoburn.
*/

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif

#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>

/* for -charset */
#include <iconv.h>
#include <langinfo.h>


#include "xorriso.h" 
#include "xorriso_private.h"
#include "xorrisoburn.h"

#include "lib_mgt.h"


int Xorriso_abort(struct XorrisO *xorriso, int flag)
{
 int ret;

 ret= burn_abort(4440, burn_abort_pacifier, "xorriso : ");
 if(ret<=0) {
   fprintf(stderr,
       "\nxorriso : ABORT : Cannot cancel burn session and release drive.\n");
   return(0);
 } 
 fprintf(stderr,
   "xorriso : ABORT : Drive is released and library is shut down now.\n");
 fprintf(stderr,
   "xorriso : ABORT : Program done. Even if you do not see a shell prompt.\n");
 fprintf(stderr, "\n");
 exit(1);
}


/* @param flag bit0= asynchronous handling (else catch thread, wait, and exit)
*/
int Xorriso_set_signal_handling(struct XorrisO *xorriso, int flag)
{
 char *handler_prefix= NULL;

 if(Xorriso__get_signal_behavior(0) != 1)
   return(2);
 handler_prefix= calloc(strlen(xorriso->progname)+3+1, 1);
 if(handler_prefix==NULL) {
   sprintf(xorriso->info_text,
           "Cannot allocate memory for for setting signal handler");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 sprintf(handler_prefix, "%s : ", xorriso->progname);
 burn_set_signal_handling(handler_prefix, NULL, (flag & 1) * 0x30);
 free(handler_prefix);
 return(1);
}


int Xorriso_startup_libraries(struct XorrisO *xorriso, int flag)
{
 int ret, major, minor, micro;
 char *queue_sev, *print_sev, reason[1024];
 struct iso_zisofs_ctrl zisofs_ctrl= {0, 6, 15};


/* First an ugly compile time check for header version compatibility.
   If everthing matches, then no C code is produced. In case of mismatch,
   intentionally faulty C code will be inserted.
*/

/* The minimum requirement of xorriso towards the libisoburn header
   at compile time is defined in xorriso/xorrisoburn.h 
     xorriso_libisoburn_req_major
     xorriso_libisoburn_req_minor
     xorriso_libisoburn_req_micro
   It gets compared against the version macros in libburn/libburn.h :
     isoburn_header_version_major
     isoburn_header_version_minor
     isoburn_header_version_micro
   If the header is too old then the following code shall cause failure of
   cdrskin compilation rather than to allow production of a program with
   unpredictable bugs or memory corruption.
   The compiler messages supposed to appear in this case are:
      error: 'LIBISOBURN_MISCONFIGURATION' undeclared (first use in this function)
      error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisoburn_dot_h_TOO_OLD__SEE_xorrisoburn_dot_c' undeclared (first use in this function)
      error: 'LIBISOBURN_MISCONFIGURATION_' undeclared (first use in this function)
*/
/* The indendation is an advise of man gcc to help old compilers ignoring */
 #if xorriso_libisoburn_req_major > isoburn_header_version_major
 #define Isoburn_libisoburn_dot_h_too_olD 1
 #endif
 #if xorriso_libisoburn_req_major == isoburn_header_version_major && xorriso_libisoburn_req_minor > isoburn_header_version_minor
 #define Isoburn_libisoburn_dot_h_too_olD 1
 #endif
 #if xorriso_libisoburn_req_minor == isoburn_header_version_minor && xorriso_libisoburn_req_micro > isoburn_header_version_micro
 #define Isoburn_libisoburn_dot_h_too_olD 1
 #endif

#ifdef Isoburn_libisoburn_dot_h_too_olD
LIBISOBURN_MISCONFIGURATION = 0;
INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisoburn_dot_h_TOO_OLD__SEE_xorrisoburn_dot_c = 0;
LIBISOBURN_MISCONFIGURATION_ = 0;
#endif

/* End of ugly compile time test (scroll up for explanation) */

 reason[0]= 0;
 ret= isoburn_initialize(reason, 0);
 if(ret==0) {
   sprintf(xorriso->info_text, "Cannot initialize libraries");
   if(reason[0])
     sprintf(xorriso->info_text+strlen(xorriso->info_text),
             ". Reason given:\n%s", reason);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(0);
 }
 ret= isoburn_is_compatible(isoburn_header_version_major,
                            isoburn_header_version_minor,
                            isoburn_header_version_micro, 0);
 if(ret<=0) {
   isoburn_version(&major, &minor, &micro);
   sprintf(xorriso->info_text,
          "libisoburn version too old: %d.%d.%d . Need at least: %d.%d.%d .\n",
          major, minor, micro,
          isoburn_header_version_major, isoburn_header_version_minor,
          isoburn_header_version_micro);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }

 xorriso->libs_are_started= 1;

 queue_sev= "ALL";
 if(xorriso->library_msg_direct_print) {

   /* >>> need option for controlling this in XorrisO.
          See also Xorriso_msgs_submit */;

   print_sev= xorriso->report_about_text;
 } else
   print_sev= "NEVER";

 iso_set_msgs_severities(queue_sev, print_sev, "libsofs : ");
 burn_msgs_set_severities(queue_sev, print_sev, "libburn : ");

 /* ??? >>> do we want united queues ? */
 /* burn_set_messenger(iso_get_messenger()); */

 isoburn_set_msgs_submit(Xorriso_msgs_submit_void, (void *) xorriso,
                         (3<<2) | 128 , 0);

 ret= Xorriso_set_signal_handling(xorriso, 0);
 if(ret <= 0)
   return(ret);

 ret = iso_zisofs_get_params(&zisofs_ctrl, 0);
 if (ret == 1) {
   xorriso->zisofs_block_size= xorriso->zisofs_block_size_default=
       (1 << zisofs_ctrl.block_size_log2);
   xorriso->zlib_level= xorriso->zlib_level_default=
       zisofs_ctrl.compression_level;
 }

 /* Second initialization. This time with libs. */
 Xorriso_preparer_string(xorriso, xorriso->preparer_id, 0);

 Xorriso_process_msg_queues(xorriso,0);
 if(reason[0]) {
   sprintf(xorriso->info_text, "%s", reason);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 }
 strcpy(xorriso->info_text, "Using ");
 strncat(xorriso->info_text, burn_scsi_transport_id(0), 1024);
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 return(1);
}


/* @param flag bit0= global shutdown of libraries */
int Xorriso_detach_libraries(struct XorrisO *xorriso, int flag)
{
 Xorriso_give_up_drive(xorriso, 3);
 if(xorriso->in_volset_handle!=NULL) { /* standalone image */
   iso_image_unref((IsoImage *) xorriso->in_volset_handle);
   xorriso->in_volset_handle= NULL;
   Sectorbitmap_destroy(&(xorriso->in_sector_map), 0);
   Xorriso_destroy_di_array(xorriso, 0);
   Xorriso_destroy_hln_array(xorriso, 0);
   xorriso->boot_count= 0;
 }
 if(flag&1) {
   if(xorriso->libs_are_started==0)
     return(0);
   isoburn_finish();
 }
 return(1);
}


/* @param flag bit0= suppress messages below UPDATE
               bit1= suppress messages below FAILURE
*/
int Xorriso_set_image_severities(struct XorrisO *xorriso, int flag)
{
 char *queue_sev, *print_sev;

 if(flag&2)
   queue_sev= "FAILURE";
 else if(flag&1)
   queue_sev= "UPDATE";
 else
   queue_sev= "ALL";
 if(xorriso->library_msg_direct_print)
   print_sev= xorriso->report_about_text;
 else
   print_sev= "NEVER";
 iso_set_msgs_severities(queue_sev, print_sev, "libisofs : ");
 return(1);
}


/* @param flag bit0=prepare for a burn run */
int Xorriso_set_abort_severity(struct XorrisO *xorriso, int flag)
{
 int ret, abort_on_number;
 char *sev_text;
 static int note_number= -1, failure_number= -1;

 if(note_number==-1)
   Xorriso__text_to_sev("NOTE", &note_number, 0);
 if(failure_number==-1)
   Xorriso__text_to_sev("FAILURE", &failure_number, 0);
 sev_text= xorriso->abort_on_text;
 ret= Xorriso__text_to_sev(xorriso->abort_on_text, &abort_on_number, 0);
 if(ret<=0)
   return(ret);
 if(abort_on_number<note_number)
   sev_text= "NOTE";
 else if(abort_on_number>failure_number)
   sev_text= "FAILURE";
 ret= iso_set_abort_severity(sev_text);
 return(ret>=0);
}


int Xorriso_report_lib_versions(struct XorrisO *xorriso, int flag)
{
 int major, minor, micro;
 int req_major, req_minor, req_micro;

 iso_lib_version(&major, &minor, &micro);
 isoburn_libisofs_req(&req_major, &req_minor, &req_micro);
 sprintf(xorriso->result_line,
         "libisofs   in use :  %d.%d.%d  (min. %d.%d.%d)\n",
         major, minor, micro, req_major, req_minor, req_micro);
 Xorriso_result(xorriso, 0);
 burn_version(&major, &minor, &micro);
 isoburn_libburn_req(&req_major, &req_minor, &req_micro);
 sprintf(xorriso->result_line,
         "libburn    in use :  %d.%d.%d  (min. %d.%d.%d)\n",
         major, minor, micro, req_major, req_minor, req_micro);
 Xorriso_result(xorriso, 0);
 strcpy(xorriso->result_line, "libburn OS adapter:  ");
 strncat(xorriso->result_line, burn_scsi_transport_id(0), 1024);
 strcat(xorriso->result_line, "\n");
 Xorriso_result(xorriso, 0);
 isoburn_version(&major, &minor, &micro);
 sprintf(xorriso->result_line,
         "libisoburn in use :  %d.%d.%d  (min. %d.%d.%d)\n",
         major, minor, micro,
         isoburn_header_version_major, isoburn_header_version_minor,
         isoburn_header_version_micro);
 Xorriso_result(xorriso, 0);
 return(1);
}


int Xorriso__sev_to_text(int severity, char **severity_name,
                         int flag)
{
 int ret;

  ret= iso_sev_to_text(severity, severity_name);
  if(ret>0)
    return(ret);
  ret= burn_sev_to_text(severity, severity_name, 0);
  if(ret>0)
    return(ret);
 *severity_name= "";
 return(0);
}


int Xorriso__text_to_sev(char *severity_name, int *severity_number, int flag)
{
 int ret= 1;

 ret= iso_text_to_sev(severity_name, severity_number);
 if(ret>0)
   return(ret);
   ret= burn_text_to_sev(severity_name, severity_number, 0);
 return(ret);
}


/* @param flag bit0= report libisofs error text
               bit1= victim is disk_path
               bit2= do not inquire libisofs, report msg_text and min_severity
*/
int Xorriso_report_iso_error(struct XorrisO *xorriso, char *victim,
                        int iso_error_code, char msg_text[], int os_errno,
                        char min_severity[], int flag)
{
 int error_code, iso_sev, min_sev, ret;
 char *sev_text_pt, *msg_text_pt= NULL;
 char sfe[6*SfileadrL];
 static int sorry_sev= -1;

 if(sorry_sev<0)
   Xorriso__text_to_sev("SORRY", &sorry_sev, 0);

 if(flag&4) {
   error_code= 0x00050000;
   Xorriso__text_to_sev(min_severity, &iso_sev, 0);
 } else {
   error_code= iso_error_get_code(iso_error_code);
   if(error_code < 0x00030000 || error_code >= 0x00040000)
     error_code= (error_code & 0xffff) | 0x00050000;
   if(flag&1)
     msg_text_pt= (char *) iso_error_to_msg(iso_error_code);
   iso_sev= iso_error_get_severity(iso_error_code);
 }
 if(msg_text_pt==NULL)
   msg_text_pt= msg_text;

 if(iso_sev >= sorry_sev && (flag & 2) && victim[0])
   Xorriso_msgs_submit(xorriso, 0, victim, 0, "ERRFILE", 0);
 sev_text_pt= min_severity;
 Xorriso__text_to_sev(min_severity, &min_sev, 0);
 if(min_sev < iso_sev && !(flag&4))
   Xorriso__sev_to_text(iso_sev, &sev_text_pt, 0);
 strcpy(sfe, msg_text_pt);
 if(victim[0]) {
   strcat(sfe, ": ");
   Text_shellsafe(victim, sfe+strlen(sfe), 0);
 }
 ret= Xorriso_msgs_submit(xorriso, error_code, sfe, os_errno, sev_text_pt, 4);
 return(ret);
}


int Xorriso_get_local_charset(struct XorrisO *xorriso, char **name, int flag)
{
 (*name)= iso_get_local_charset(0);
 return(1);
}


int Xorriso_set_local_charset(struct XorrisO *xorriso, char *name, int flag)
{
 int ret;
 char *nl_charset, sfe[5 * SfileadrL];
 iconv_t iconv_ret= (iconv_t) -1;

 nl_charset= nl_langinfo(CODESET);
 if(name == NULL)
   name= nl_charset;

 if(name != NULL) {
   iconv_ret= iconv_open(nl_charset, name);
   if(iconv_ret == (iconv_t) -1)
     goto cannot;
   else
     iconv_close(iconv_ret);
 }
 ret= iso_set_local_charset(name, 0);
 if(ret <= 0) {
cannot:;
   sprintf(xorriso->info_text,
           "-local_charset: Cannot assume as local character set: %s",
           Text_shellsafe(name, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   return(0);
 }
 sprintf(xorriso->info_text, "Local character set is now assumed as: %s",
         Text_shellsafe(name, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 return(1);
}


int Xorriso_process_msg_queues(struct XorrisO *xorriso, int flag)
{
 int ret, error_code= 0, os_errno= 0, count= 0, pass, imgid, tunneled;
 char severity[80];

 if(!xorriso->libs_are_started)
   return(1);
 for(pass= 0; pass< 2; pass++) {
   while(1) {
     tunneled= 0;
     if(pass==0)
       ret= iso_obtain_msgs("ALL", &error_code, &imgid,
                            xorriso->info_text, severity);
     else {
       ret= burn_msgs_obtain("ALL", &error_code, xorriso->info_text, &os_errno,
                             severity);
       if((error_code>=0x00030000 && error_code<0x00040000) ||
          (error_code>=0x00050000 && error_code<0x00060000))
         tunneled= -1; /* "libisofs:" */
       else if(error_code>=0x00060000 && error_code<0x00070000)
         tunneled= 1;  /* "libisoburn:" */
     }
     if(ret<=0)
   break;

     /* <<< tunneled MISHAP from libisoburn through libburn
            or well known error codes of MISHAP events
            With libburn-0.4.4 this is not necessary */
     if(error_code==0x5ff73 || error_code==0x3ff73 ||
        error_code==0x3feb9 || error_code==0x3feb2)
       strcpy(severity, "MISHAP");
     else if(error_code==0x51001)
       strcpy(severity, "ERRFILE");

     Xorriso_msgs_submit(xorriso, error_code, xorriso->info_text, os_errno, 
                         severity, ((pass+tunneled)+1)<<2);
     count++;
   }
 }
 if(xorriso->library_msg_direct_print && count>0) {
   sprintf(xorriso->info_text,"   (%d library messages repeated by xorriso)\n",
           count);
   Xorriso_info(xorriso, 0);
 }
 return(1);
}


int Xorriso_md5_start(struct XorrisO *xorriso, void **ctx, int flag)
{
 int ret;

 ret= iso_md5_start(ctx);
 if(ret == 1)
   return(1);
 Xorriso_no_malloc_memory(xorriso, NULL, 0);
 return(-1);
}


int Xorriso_md5_compute(struct XorrisO *xorriso, void *ctx,
                        char *data, int datalen, int flag)
{
 iso_md5_compute(ctx, data, datalen);
 return(1);
}


int Xorriso_md5_end(struct XorrisO *xorriso, void **ctx, char md5[16],
                    int flag)
{
 int ret;

 ret= iso_md5_end(ctx, md5);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0)
   return(0);
 return(1);
}


/* @param flag  bit0= avoid library calls
 */
int Xorriso_preparer_string(struct XorrisO *xorriso, char xorriso_id[129],
                            int flag)
{
 int major, minor, micro;

 xorriso_id[0]= 0;
 sprintf(xorriso_id, "XORRISO-%d.%d.%d",
         Xorriso_header_version_majoR, Xorriso_header_version_minoR,
         Xorriso_header_version_micrO);
 if(strlen(xorriso_id) + strlen(Xorriso_timestamP) < 128)
   strcat(xorriso_id, Xorriso_timestamP);
 if(flag & 1)
   return(1);
 isoburn_version(&major, &minor, &micro);
 if(strlen(xorriso_id) < 100)
   sprintf(xorriso_id + strlen(xorriso_id),
           ", LIBISOBURN-%d.%d.%d", major, minor, micro);
 iso_lib_version(&major, &minor, &micro);
 if(strlen(xorriso_id) < 100)
   sprintf(xorriso_id + strlen(xorriso_id),
           ", LIBISOFS-%d.%d.%d", major, minor, micro);
 burn_version(&major, &minor, &micro);
 if(strlen(xorriso_id) < 100)
   sprintf(xorriso_id + strlen(xorriso_id),
           ", LIBBURN-%d.%d.%d", major, minor, micro);
 return(1);
}

