#include <stdio.h>
#include <assert.h>
#include <string.h>

#include "argflags.h"
#include "npaconsts.h"
#include "npastructs.h"
#include "npaoids.h"
#include "structfill.h"
#include "compat.h"

#define MAXSTR 1024

#define ERR_NO_RESPONSE 8 
#define ERR_SOCK_ERROR 9 
#define ERR_NOT_PRINTER 10
#define ERR_UNKNOWN_DEVICE 11

extern char *progname;

ConnectionInfo DUMMY_CONNINFO;
HrStorageTable BHSID;

static const char HPSTR[]="JETDIRECT";
static const char LEXMARKSTR[]="Lexmark";
static const char TEKTRONIXSTR[]="Tektronix";
static const char XEROXDC230STR[]="131;C1H011131;";
static const char XEROXDC230STR2[]=";C1H017730;";
static const char XEROXDC265STR[]="3UP060485";
static const char XEROXSTR[]="Xerox";
static const char QMSSTR[]="QMS";
static const char IBMSTR[]="IBM";
static const char EFISTR[]="EFI Fiery Color Printer Server";
static const char EFISTR2[]="EFI Fiery Server ZX";
static const char FUJISTR[]="Able Model-PRII";

static const char *TCPSTATES[]={NULL,"closed","listen","synSent",
				"synReceived","established","finWait1",
				"finWait2","closeWait","lastAck",
				"closing","timeWait"};
static const char *HRSTATUSSTATES[]={NULL,"other","unknown","idle",
				     "printing","warmup"};
static const char *HPCFGSOURCESTATES[]={"bootp","manual","unknown",
					"manual","bootp","dhcp"};

void handle_unrecoverable(char *progname,char *hostname,
			  SNMP_error_unrecoverable &error){
  char errnum=0;
  switch(error){
  case SNMPERR_NoResponse:
    fprintf(stderr,"%s: No response from %s\n",progname,hostname);
    errnum=ERR_NO_RESPONSE;
    break;
  case SNMPERR_SocketErr:
    char buf[50];
    snprintf(buf,50,"%s: %u.%u.%u.%u",progname,hostname);
    perror(buf);
    errnum=ERR_SOCK_ERROR;
    break;
  }
  int *i=new int;
  *i=errnum;
  pthread_exit(i);
}

void usage(){
  fprintf(stderr,"npadmin [-c community] {options} printername\n");
}

void not_sup(char *option,char* hostname){
  fprintf(stderr,"%s: %s option not supported on this printer type.\n",
	  hostname,option);
}

char *decode_status(long stat){
  char *buf=new char[256];
  memset(buf,0,256);
  if(stat&64) strcat(buf,"Transitioning ");
  if(stat&32) strcat(buf,"Off-Line ");
  if(stat&16) strcat(buf,"Critical Alerts ");
  if(stat&8) strcat(buf,"Non-Critical Alerts ");
  switch(stat&7){
  case 0:
    strcat(buf,"Available and Idle");
    break;
  case 2:
    strcat(buf,"Available and Standby");
    break;
  case 4:
    strcat(buf,"Available and Active");
    break;
  case 6:
    strcat(buf,"Available and Busy");
    break;
  case 1:
    strcat(buf,"Unavailable and OnRequest");
    break;
  case 3:
    strcat(buf,"Unavailable because Broken");
    break;
  case 5:
    strcat(buf,"Unknown");
    break;
  default:
    assert(0); //undefined state
  }
  return buf;
}

/* XXX: This is an ugly evil nasty hack. Basically HP 4 series
   printers and below do not support the hostmib or the printmib 
   but yet we still want to be able to get their current status
   information as well as the information in the display and so we
   need to get it out of the private mibs. This seems like the 
   right place to do it because it is before the hostmib 
   processing is run and after we know what kind of printer we
   are dealing with.  */
void do_hppriv_get(SNMP_session &session, unsigned long *argflags,
		   unsigned long &operflags){
  SNMP_structFiller hpprivreq(session);
  HPPrivInfo privinfo;
  char buf[MAXSTR];
  if(CK_STATUS_FLAG)
    hpprivreq.append(HPNPSYSSTATUSMESSAGE,STRING_TAG,
		     (char*)&(privinfo.status)-(char*)&privinfo);
  if(CK_DISPLAY_FLAG)
    hpprivreq.append(HPNPGDSTATUSDISPLAY,STRING_TAG,
		     (char*)&(privinfo.frontpanel)-(char*)&privinfo);

  try{
    hpprivreq.get(&privinfo);
  } catch (SNMP_error_unrecoverable &error){
    handle_unrecoverable(progname, session.Hostname(),error);
  }
  
  /* reset the flags -- we got the message but we don't want it to
     propegating down. */
  if(CK_STATUS_FLAG){
    RST_STATUS_FLAG;
    if(!(CK_HOSTMIB_FLAGS))
      operflags&=~NEED_HOSTMIB_FLAG;
    snprintf(buf,MAXSTR,"status=\"%s\";",privinfo.status);
    session.printstr(argflags,NEEDNL_ENTRY,buf);
  }
  if(CK_DISPLAY_FLAG){
    RST_DISPLAY_FLAG;
    if(!(CK_PRINTMIB_FLAGS))
      operflags&=~NEED_PRINTMIB_FLAG;
    snprintf(buf,MAXSTR,"displayBufferText=\"%s\";\n",privinfo.frontpanel);
    session.printstr(argflags,NEEDNL_ENTRY,buf);
  }
}

/* XXX: Another ugly evil nasty hack. */
void do_hppriv_set(SNMP_session &session, unsigned long *argflags,
		   unsigned long &operflags,char **optparam){
  OidSeq hpprivsets;
  if(CK_REBOOT_FLAG)
    hpprivsets.append(HPREBOOT,INT_TAG,1);

  if(CK_UPDATEFIRM_FLAG){
    //  fprintf(stderr,"debug: %s \n",optparam[UPDATEFIRM_PARAM]);
    hpprivsets.append(HPICFDOWNLOADSTATUS,INT_TAG,4); // create and go
    char prefix[]="1.3.6.1.6.1.1";
    hpprivsets.append(HPICFDOWNLOADTDOMAIN,OID_TAG,prefix,13);
    
    char *server,*filename;
    // fprintf(stderr, "debug: %s \n",optparam[UPDATEFIRM_PARAM]);
    server=strdup(optparam[UPDATEFIRM_PARAM]);
    assert(server);
    filename=strchr(server,':');
    assert(filename);
    *filename=0;
    filename++;
    
    struct hostent *he;
    assert((he=gethostbyname(server))!=NULL);
    char *svrip=strdup(he->h_addr);
    assert(svrip!=NULL && (svrip=(char*)realloc(svrip,he->h_length+3)));
    svrip[he->h_length]=0;
    svrip[he->h_length+1]=0x45;
    svrip[he->h_length+2]=0;
    hpprivsets.append(HPICFDOWNLOADTADDRESS,STRING_TAG,svrip,he->h_length+2);
    hpprivsets.append(HPICFDOWNLOADFILENAME,STRING_TAG,filename,
		      strlen(filename)+1);
    free(server);
  }

  OidSeq *response=session.set(&hpprivsets);
  RST_REBOOT_FLAG;
}

void do_general_get(SNMP_session &session, unsigned long *argflags,
		unsigned long &operflags,char **optparam){
  // setup the requests
  SNMP_structFiller getreq(session);
  GeneralPrinterInfo gpi;
  if(CK_VENDOR_FLAG || CK_MODEL_FLAG || CK_HOSTMIB_FLAG || CK_PRINTMIB_FLAG ||
     operflags&(NEED_HOSTMIB_FLAG | NEED_PRINTMIB_FLAG | NEED_VENDOR_FLAG))
    getreq.append(SYSDESC,STRING_TAG,(char*)&gpi.sysDesc-(char*)&gpi); 
  //vendor done, model done for lexmarks
  if(CK_CONTACT_FLAG){
    getreq.append(SYSCONTACT,STRING_TAG,(char*)&gpi.sysContact-(char*)&gpi);
    getreq.append(SYSLOCATION,STRING_TAG,(char*)&gpi.sysLocation-
		  (char*)&gpi); 
    // contact done
  }
  if(CK_NETCONFIG_FLAG){
    getreq.append(IFPHYSICALADDRESS,STRING_TAG,(char*)&gpi.hwaddr-
		  (char*)&gpi);
    getreq.append(IFTYPE,INT_TAG,(char*)&gpi.iftype-(char*)&gpi);
    getreq.append(IPROUTENEXTHOP,IPADDR_TAG,(char*)&gpi.gateway-(char*)&gpi);

    char maskbuf[50]; // should be big enough
    char *connhost=session.ConnHost();
    int len=snprintf(maskbuf,50,"%s.%u.%u.%u.%u",IPENTNETMASK_BASE,
		 ((unsigned)connhost[0] & 0xff),
		 ((unsigned)connhost[1] & 0xff),
		 ((unsigned)connhost[2] & 0xff),
		 ((unsigned)connhost[3] & 0xff));
    
    getreq.append(maskbuf,IPADDR_TAG,(char*)&gpi.netmask-(char*)&gpi); 
  } // end of if(CK_NETCONFIG_FLAG)
  
  /* all the oids that fit in one packet have been loaded send 
     the request */
  char retry=0;
  do{
    try{
      getreq.get(&gpi);
      retry=0;
    } catch(SNMP_error_unrecoverable &error){
      handle_unrecoverable(progname, session.Hostname(),error);
    } catch(SNMP_error_oid *error){
      assert(retry<=2);
      if(*error==NOCONSTCAST IPROUTENEXTHOP){
	getreq.remove(IPROUTENEXTHOP);
	retry++;
      } else if(*error==SYSDESC){
	fprintf(stderr, 
		"Warning: %s does not have a system.sysDescr.0 probably Windows NT. Skipping.\n",
		session.Hostname());
	int *i=new int;
	assert(i!=NULL);
	*i=-3;
	pthread_exit(i);
      } else if(*error==SYSCONTACT || *error==SYSLOCATION){
	getreq.remove(SYSCONTACT);
	getreq.remove(SYSLOCATION);
	RST_CONTACT_FLAG;
	fprintf(stderr,
		"Warning: %s does't provide system.sys%s ignoring --contact.\n",
		session.Hostname(),
		*error==SYSCONTACT?"Contact":"Location");
	retry++;
      } else {
	fprintf(stderr,"debug: Bad things from %s\n",session.Hostname());
	assert(0);
      }
    }
  }while(retry);

  char buf[MAXSTR];
 
  // print out netconfig info
  if(CK_NETCONFIG_FLAG){
    /* make sure that we have the right interface basically this 
       is a problem for the lexmarks because they have a psudo 
       interface at .1 */
    if(gpi.iftype!=6){ // 6 just happens to be the # for ethernet
      SNMP_structFiller ifreq(session);
      ifreq.append(IFPHYSICALADDRESS_BASE,STRING_TAG,(char*)&gpi.hwaddr-
		   (char*)&gpi);
      ifreq.append(IFTYPE_BASE,INT_TAG,(char*)&gpi.iftype-(char*)&gpi);
      while(gpi.iftype!=6)
	try{
	  if(ifreq.get_next(&gpi)==NULL){
	    fprintf(stderr,"Warning: %s doesn't appear to have an ethernet "
		    "interface. Can't detect hwaddr.\n",session.Hostname());
	    break;
	  }
	} catch (SNMP_error_unrecoverable &error){
	  handle_unrecoverable(progname, session.Hostname(),error);
	}
    }
    
    /* this line expects the ip addresses to always be 4 bytes. */
    char *connhost=session.ConnHost();
    snprintf(buf,MAXSTR,"ipaddr=\"%u.%u.%u.%u\";netmask=\"%u.%u.%u.%u\";",
	     ((unsigned)connhost[0] & 0xff),((unsigned)connhost[1] & 0xff),
	     ((unsigned)connhost[2] & 0xff),((unsigned)connhost[3] & 0xff),
	     ((unsigned)gpi.netmask[0] & 0xff),
	     ((unsigned)gpi.netmask[1] & 0xff),
	     ((unsigned)gpi.netmask[2] & 0xff),
	     ((unsigned)gpi.netmask[3] & 0xff));
    session.printstr(argflags,NEEDNL_ENTRY,buf);

    if(gpi.iftype==6){
      snprintf(buf,MAXSTR,"hwaddr=\"%02x:%02x:%02x:%02x:%02x:%02x:\";",
	       ((unsigned)gpi.hwaddr[0] & 0xff),
	       ((unsigned)gpi.hwaddr[1] & 0xff),
	       ((unsigned)gpi.hwaddr[2] & 0xff),
	       ((unsigned)gpi.hwaddr[3] & 0xff),
	       ((unsigned)gpi.hwaddr[4] & 0xff),
	       ((unsigned)gpi.hwaddr[5] & 0xff));
      session.printstr(argflags,NEEDNL_ENTRY,buf);
    }
    
    if(gpi.gateway!=NULL){
      snprintf(buf,MAXSTR,"gateway=\"%u.%u.%u.%u\";",
	       ((unsigned)gpi.gateway[0] & 0xff),
	       ((unsigned)gpi.gateway[1] & 0xff),
	       ((unsigned)gpi.gateway[2] & 0xff),
	       ((unsigned)gpi.gateway[3] & 0xff));
      session.printstr(argflags,NEEDNL_ENTRY,buf);
    }else{
	fprintf(stderr,
		"Warning: %s did not report a default gateway.\n",
		session.Hostname());
    }
  }
  /* ------ model and vendor ---------------------------------- */
  if(gpi.sysDesc){
    if(strstr(gpi.sysDesc,HPSTR)){
      operflags|=HP_VENDOR;

      /* This could be located in down in the section where there 
	 it is only executed if it is run on a 4 series printer 
	 that  doesn't support the host mib but I am having some 
	 trouble with sets on the 5 series printers and so I think 
	 that it will be better to put it up here for the time 
	 being.*/
      if(CK_REBOOT_FLAG || CK_UPDATEFIRM_FLAG)
	do_hppriv_set(session,argflags,operflags,optparam);

      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"HP\";");

      /* damn hp makes us go down to the private enterprises area 
	 to find out the model. The new 5 series printers are a bit
	 easier but the 4 series don't support the host mib. */
      if(CK_MODEL_FLAG || CK_HOSTMIB_FLAG || CK_PRINTMIB_FLAG || 
	 operflags&(NEED_HOSTMIB_FLAG | NEED_PRINTMIB_FLAG)){
	OidSeq modelreq(1,HPINFO);
	OidSeq *modelresp;
	try{
	  //fputs("debug: getting modelstr\n",stderr);
	  modelresp=session.get(&modelreq);
	} catch (SNMP_error_unrecoverable &error){
	  handle_unrecoverable(progname, session.Hostname(),error);
	}
	assert(modelresp);
	BerBase *hpinfo=modelresp->value(HPINFO);
	assert(hpinfo);
	assert(hpinfo->type()==STRING_TAG);
	char hpbuf[256];
	char buf2[100];
	char *begin;
	((BerString*)hpinfo)->copy(hpbuf, 256);
	if(((BerString*)hpinfo)->strlen()==0){
	  fprintf(stderr,"Warning: %s model not reported.\n",
		  session.Hostname());
	  if(CK_STATUS_FLAG || CK_DISPLAY_FLAG) {
	    do_hppriv_get(session,argflags,operflags);
	  }
	} else {
	  if((begin=strstr(hpbuf, "MODEL:"))!=NULL){
	    begin+=6; 
	    begin+=!strncmp(begin,"HP ",3)?3:0; 
	  } else if((begin=strstr(hpbuf,"MDL:"))
		    !=NULL){
	    /* work around the fact that a 1600cm doesn't have 
	       MODEL it has MDL */
	    begin+=4; //skip past "MDL:"
	  } else {
	    fprintf(stderr,
		    "%s: Can't find \"MODEL:\" or \"MDL:\" in \"%s\"\n",
		    session.Hostname(),hpbuf);
	    int *i=new int;
	    assert(i);
	    *i=-3;
	    pthread_exit(i);
	  }
	  
	  char *end=strchr(begin,';');
          if (end==NULL)
		end=begin+strlen(begin);
	  strncpy(buf2,begin,end-begin);
	  buf2[end-begin]='\0';
	  if(CK_MODEL_FLAG){
	    snprintf(buf,MAXSTR,"model=\"%s\";",buf2);
	    session.printstr(argflags,NEEDNL_ENTRY,buf);
	  }

	  /* this line will probably have to be tweaked a bit I 
	     basically believe all the HP printers except for the 
	     5 series and the *000 series do not support the host 
	     or printmib */
	  /* We need to come up with a better way to do this :-) */
	  if((strstr(buf2,"000") && !strstr(buf2,"2000")) || 
	     (strstr(buf2,"LaserJet 5") && !strstr(buf2,"Color") && 
	      !strstr(gpi.sysDesc,"JETDIRECT EX")) ||
	     strstr(buf2,"8100") || strstr(buf2, "4500") || 
	     strstr(buf2, "8500") || strstr(buf2, "4050")){
	    operflags|=HAS_PRINTMIB_FLAG;
	    // work around acient broken firmware
	    if(!strstr(gpi.sysDesc,"EEPROM A.03."))
	      operflags|=HAS_HOSTMIB_FLAG;
	    else
	      fprintf(stderr,
		      "Warning: %s has ancient firmware. Please upgrade it!\n",
		      session.Hostname());
	  } else {
	    if(CK_STATUS_FLAG || CK_DISPLAY_FLAG) 
	      do_hppriv_get(session,argflags,operflags);
	    if (CK_LANGUAGES_FLAGS){
	      if((begin=strstr(hpbuf,
			   "COMMAND SET:"))!=NULL){
		begin+=12;
	      } else if((begin=strstr(hpbuf,"CMD:"))
	  	            !=NULL){
		begin+=4;
	      }
	      end=strchr(begin,';');
              if (end==NULL)
                end=begin+strlen(begin);
	      strncpy(buf2,begin,end-begin);
	      buf2[end-begin]='\0';
	      end=buf2-1;
	      do
	      {
		begin=end+1;
	        end=strchr(begin, ',');
	      	if (end)
		  *end='\0';

	        if(CK_POSTSCRIPT_FLAG && strcmp(begin, "POSTSCRIPT")==0) {
		  session.printstr(argflags,NEEDNL_ENTRY,"postscript=\"Y\";");
                  RST_POSTSCRIPT_FLAG;
	        }

	        if(CK_PCL_FLAG && strcmp(begin, "PCL")==0) {
		  session.printstr(argflags,NEEDNL_ENTRY,"pcl=\"Y\";");
                  RST_PCL_FLAG;
	        }

	        if(CK_HPGL_FLAG && strcmp(begin, "HP-GL")==0) {
		  session.printstr(argflags,NEEDNL_ENTRY,"hpgl=\"Y\";");
                  RST_HPGL_FLAG;
	        }

	        if(CK_PJL_FLAG && strcmp(begin, "PJL")==0) {
		  session.printstr(argflags,NEEDNL_ENTRY,"pjl=\"Y\";");
                  RST_PJL_FLAG;
	        }

	      } while(end);
	    }
	  }
	}
	delete modelresp;
      }
    } else if(!strncmp(gpi.sysDesc,LEXMARKSTR,7)){
      operflags|=LEX_VENDOR;
      operflags|=HAS_HOSTMIB_FLAG|HAS_PRINTMIB_FLAG;
      if(CK_VENDOR_FLAG){
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"Lexmark\";");
      }
      if(CK_MODEL_FLAG){
	char buf2[50];
	char *begin=gpi.sysDesc+8;
	/* this is a cool thing. On the lexmarks that I have 
	   looked at they seperate the version and the model with 
	   a double space. This makes parsing easier. */
	int len=strstr(begin,"  ")-begin;
	strncpy(buf2,begin,len);
	buf2[len]=0;
	snprintf(buf,MAXSTR,"model=\"%s\";",buf2);
	session.printstr(argflags,NEEDNL_ENTRY,buf);
      }
    } else if(!strncmp(gpi.sysDesc,TEKTRONIXSTR,9)){
      char tekbuf[256];
      operflags|=HAS_HOSTMIB_FLAG|HAS_PRINTMIB_FLAG|TEK_VENDOR;
      OidSeq modelreq(1,HRDEVICEDESC);
      OidSeq *modelresp;
      try{
	modelresp=session.get(&modelreq);
      } catch (SNMP_error_unrecoverable &error){
	handle_unrecoverable(progname, session.Hostname(),error);
      } catch(SNMP_error_oid *error){
	char *connhost=session.ConnHost();
	fprintf(stderr,
	       "Warning: Tektronix printer %s does not support the hostmib.\n",
		session.Hostname());
	operflags&=~(HAS_HOSTMIB_FLAG|HAS_PRINTMIB_FLAG);
	modelreq.remove(HRDEVICEDESC);
	modelreq.append(TKMODEL);
	try{
	  modelresp=session.get(&modelreq);
	} catch (SNMP_error_unrecoverable &error){
	  handle_unrecoverable(progname, session.Hostname(),error);
	} catch (SNMP_error_oid *error){
	  assert(0);
	}
	if(CK_MODEL_FLAG){
	  RST_MODEL_FLAG;
	  BerBase *hrdevdesc=modelresp->value(TKMODEL);
	  assert(hrdevdesc->type()==STRING_TAG);
	  ((BerString*)hrdevdesc)->copy(tekbuf, 256);
	  snprintf(buf,MAXSTR,"model=\"%s\";",tekbuf);
	  session.printstr(argflags,NEEDNL_ENTRY,buf);
	  delete modelresp;
	}
      }
      if(CK_VENDOR_FLAG){
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"Tektronix\";");
      }
      if(CK_MODEL_FLAG){
	assert(modelresp);
	BerBase *hrdevdesc=modelresp->value(HRDEVICEDESC);
	assert(hrdevdesc->type()==STRING_TAG);
        ((BerString*)hrdevdesc)->copy(tekbuf, 256);
	char *begin=strstr(tekbuf,"Inc., ");
	if (begin) {
	  char buf2[50];
          begin+=6;
	  int len=strchr(begin,',')-begin;
	  strncpy(buf2,begin,len);
	  buf2[len]=0;
	  snprintf(buf,MAXSTR,"model=\"%s\";",buf2);
	  session.printstr(argflags,NEEDNL_ENTRY,buf);
	} else {
	  fprintf(stderr,
		    "%s: Can't find \"Inc., \" in \"%s\"\n",
		    session.Hostname(),tekbuf);
	}
	delete modelresp;
      }      
    } else if(!strncmp(gpi.sysDesc,XEROXSTR,5)){
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"Xerox\";");
      if(strstr(gpi.sysDesc,"???")){
	if(CK_MODEL_FLAG)
	  not_sup("model",session.Hostname());
	operflags|=XER_VENDOR;
      } else {
	if(CK_MODEL_FLAG){
	  char *model=strchr(gpi.sysDesc,' ')+1;
	  snprintf(buf,MAXSTR,"model=\"%s\";",model);
	  session.printstr(argflags,NEEDNL_ENTRY,buf);
	}
	operflags|=HAS_HOSTMIB_FLAG|HAS_PRINTMIB_FLAG|XER_VENDOR;
      }
    } else if(!strncmp(gpi.sysDesc,XEROXDC230STR,14) || 
	      !strncmp(gpi.sysDesc,XEROXDC230STR2,10)){
      operflags|=HAS_HOSTMIB_FLAG|HAS_PRINTMIB_FLAG|XER_VENDOR;
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"Xerox\";");
      if(CK_MODEL_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,
			 "model=\"Document Centre 230ST\";");
    } else if(!strncmp(gpi.sysDesc,XEROXDC265STR,9)){
      operflags|=HAS_HOSTMIB_FLAG|HAS_PRINTMIB_FLAG|XER_VENDOR;
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"Xerox\";");
      if(CK_MODEL_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,
			 "model=\"Document Centre 265\";");      
    } else if(!strncmp(gpi.sysDesc,QMSSTR,3)){
      operflags|=QMS_VENDOR;
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"QMS\";");
      if(CK_MODEL_FLAG){
	strtok(gpi.sysDesc,";");
	snprintf(buf,MAXSTR,"model=\"%s\";",gpi.sysDesc+4);
	session.printstr(argflags,NEEDNL_ENTRY,buf);
      }      
    } else if(!strncmp(gpi.sysDesc,IBMSTR,3)){
      operflags|=IBM_VENDOR;
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"IBM\";");
      if(CK_MODEL_FLAG){
	strtok(gpi.sysDesc+4," ");
	snprintf(buf,MAXSTR,"model=\"%s\";",gpi.sysDesc+4);
	session.printstr(argflags,NEEDNL_ENTRY,buf);
      }
    } else if(!strncmp(gpi.sysDesc,EFISTR,strlen(EFISTR)) || 
	      !strncmp(gpi.sysDesc,EFISTR2,strlen(EFISTR))){
      operflags|=HAS_HOSTMIB_FLAG|EFI_VENDOR;
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"EFI\";");
      if(CK_MODEL_FLAG)
	not_sup("model",session.Hostname());
    } else if(!strncmp(gpi.sysDesc,FUJISTR,strlen(FUJISTR))){
      /* I think that this was a mistake and so I commented this
	 out. However, there might be a fuji printer out there
	 somewhere which does support the host mib and the print
	 mib. If there is then we will have to make a much more
	 exacting test. */
      // operflags|=HAS_HOSTMIB_FLAG|FUJI_VENDOR;
      if(CK_VENDOR_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"vendor=\"Fuji\";");
      if(CK_MODEL_FLAG)
	session.printstr(argflags,NEEDNL_ENTRY,"model=\"Able PRII\";");
      /* Hopefully none of these give false positives. This sort of filter's
	 out some of the more obvious non-printer snmp services */
     } else if(!strstr(gpi.sysDesc,"HP-UX") || !strstr(gpi.sysDesc,"HPUX") || 
	       !strstr(gpi.sysDesc,"Windows NT") || 
	       !strstr(gpi.sysDesc,"Sun SNMP Agent") ||
	       !strstr(gpi.sysDesc,"SunOS") || 
	       !strstr(gpi.sysDesc,"Macintosh") || 
	       !strstr(gpi.sysDesc,"UNIX")){
       fprintf(stderr,
	       "Warning: %s does not appear to be a printer. Skipping.\n",
	       session.Hostname());
      int *i=new int;
      assert(i);
      *i=ERR_NOT_PRINTER;
      pthread_exit(i);       
     } else {
      char *connhost=session.ConnHost();
      fprintf(stderr,"Warning: %s: unknown printer vendor. Describes itself "
	      "as \"%s\"\n",session.Hostname(),gpi.sysDesc);
      int *i=new int;
      assert(i);
      *i=ERR_UNKNOWN_DEVICE;
      pthread_exit(i);
    }
  }

  /* ------- print out the contact information --------------------------- */
  if(CK_CONTACT_FLAG){
    snprintf(buf,MAXSTR,"contact=\"%s\";location=\"%s\";",
	     gpi.sysContact?gpi.sysContact:"",
	     gpi.sysLocation?gpi.sysLocation:"");
    session.printstr(argflags,NEEDNL_ENTRY,buf);
  }

  if(CK_HOSTMIB_FLAG){
    snprintf(buf,MAXSTR,"hostmib=\"%c\";",operflags&HAS_HOSTMIB_FLAG?'Y':'N');
    session.printstr(argflags,NEEDNL_ENTRY,buf);
  }
  if(CK_PRINTMIB_FLAG){
    snprintf(buf,MAXSTR,"printmib=\"%c\";",
	     operflags&HAS_PRINTMIB_FLAG?'Y':'N');
    session.printstr(argflags,NEEDNL_ENTRY,buf);
  }
}

void do_connections(unsigned long *argflags,SNMP_session &session,
		    ConnectionInfoRequest *cir){
  ConnectionInfoRequest *cur=cir;
  
  while(cur){
    SNMP_table conntabreq(session, sizeof(ConnectionInfo));
    assert(cur->statebuf=new char[1024]);
    int len=strlen(TCPCONNSTATE_BASE);
    /* we can assume that this worked otherwise the session would
       have failed above and we can also assume that printers 
       only have one ip address right? */
    strcpy(cur->statebuf,TCPCONNSTATE_BASE);
    // append the ipaddress and the port to the base oid
    len+=sprintf(cur->statebuf+strlen(cur->statebuf),".%u.%u.%u.%u.%u",
		 ((unsigned)session.ConnHost()[0] & 0xff),
		 ((unsigned)session.ConnHost()[1] & 0xff),
		 ((unsigned)session.ConnHost()[2] & 0xff),
		 ((unsigned)session.ConnHost()[3] & 0xff),
		 ((unsigned)cur->port & 0xffff))+1;
    cur->statebuf=(char*)realloc(cur->statebuf,len+1);
    conntabreq.append(cur->statebuf,INT_TAG,
		      ((char*)&(DUMMY_CONNINFO.connState))-
		      (char*)&DUMMY_CONNINFO);
      
    cur->remaddrbuf=new char[1024];
    len=strlen(TCPCONNREMADDRESS_BASE);
    char *p=session.ConnHost();
    strcpy(cur->remaddrbuf,TCPCONNREMADDRESS_BASE);
    // append the ipaddress and the port to the base oid
    len+=sprintf(cur->remaddrbuf+strlen(cur->remaddrbuf),".%u.%u.%u.%u.%u",
		 ((unsigned)p[0] & 0xff),((unsigned)p[1] & 0xff),
		 ((unsigned)p[2] & 0xff),((unsigned)p[3] & 0xff),
		 ((unsigned)cur->port & 0xffff))+1;
    cur->remaddrbuf=(char*)realloc(cur->remaddrbuf,len+1);
    conntabreq.append(cur->remaddrbuf,IPADDR_TAG,
		      ((char*)&(DUMMY_CONNINFO.remhost))-
		      (char*)&DUMMY_CONNINFO);
    unsigned int numrecords;
    try{
      cur->conninfo=(ConnectionInfo*)conntabreq.get(numrecords);
    } catch (SNMP_error_unrecoverable &error){
      handle_unrecoverable(progname, session.Hostname(),error);
    }    
    cur=cur->next;
  }  

  // print out connection info
  for(cur=cir;cur!=NULL;cur=cur->next){
    char buf[MAXSTR];
    assert(cur->conninfo->connState>0 && cur->conninfo->connState<=11);
    snprintf(buf,MAXSTR,
	     "port=\"%u\";state=\"%s\";remotehost=\"%u.%u.%u.%u\"\n",
	     cur->port,TCPSTATES[cur->conninfo->connState],
	     ((unsigned)cur->conninfo->remhost[0] & 0xff),
	     ((unsigned)cur->conninfo->remhost[1] & 0xff),
	     ((unsigned)cur->conninfo->remhost[2] & 0xff),
	     ((unsigned)cur->conninfo->remhost[3] & 0xff));
    session.printstr(argflags,NEEDNL_LINE,buf);
  }
}

void do_hostmib_get(SNMP_session &session, unsigned long *argflags,
		    unsigned long &operflags){
  if(CK_MEMORY_FLAG || CK_STATUS_FLAG){
    HostPrinterInfo hprinfo;
    char buf[MAXSTR];

    SNMP_structFiller getreq(session);
    if(CK_MEMORY_FLAG)
      getreq.append(HRMEMORYSIZE,INT_TAG,
		    (char*)&(hprinfo.memsize)-(char*)&hprinfo);
    if(CK_STATUS_FLAG)
      getreq.append(HRPRINTERSTATUS,INT_TAG,
		    (char*)&(hprinfo.prstatus)-(char*)&hprinfo);

    char retry=0;
    do{
      try{
	getreq.get(&hprinfo);
	retry=0;
      } catch(SNMP_error_unrecoverable &error){
	handle_unrecoverable(progname, session.Hostname(),error);
      } catch(SNMP_error_oid *error){
	assert(retry<=2); //something is bad the request is looping
	if(*error==HRMEMORYSIZE){
	  /* this is a hack that takes care of a xerox printer that
	     stores it in .1 rather than .0 */
	  getreq.remove(HRMEMORYSIZE);
	  getreq.append(HRMEMORYSIZE1,INT_TAG,
			(char*)&(hprinfo.memsize)-(char*)&hprinfo);
	  retry++;
	} else if(*error==HRPRINTERSTATUS && operflags&HP_VENDOR){
	  getreq.remove(HRPRINTERSTATUS);
	  fprintf(stderr,
		  "Warning: %s has an old firmware. Please upgrade it!\n",
		  session.Hostname());
	  do_hppriv_get(session,argflags,operflags);
	  if(CK_MEMORY_FLAG) // only retry if there is another oid
	    retry++;
	} else {
	  assert(0); //tell me about this something went really wrong
	}
      }
    }while(retry);

    if(CK_MEMORY_FLAG){
      snprintf(buf,MAXSTR,"memsize=\"%u\";",hprinfo.memsize);
      session.printstr(argflags,NEEDNL_ENTRY,buf);
    }
    if(CK_STATUS_FLAG){
      assert(hprinfo.prstatus>=0 && hprinfo.prstatus<=5 && 
	     HRSTATUSSTATES[hprinfo.prstatus]!=NULL);
      snprintf(buf,MAXSTR,"status=\"%s\";",HRSTATUSSTATES[hprinfo.prstatus]);
      session.printstr(argflags,NEEDNL_ENTRY,buf);
    }
  }
  if(CK_STORAGE_FLAG){
    HrStorageTable *storage;
    unsigned int len;
    SNMP_table stortabreq(session,sizeof(HrStorageTable));
    stortabreq.append(HRSSTORAGEDESCR_BASE,STRING_TAG,
		      (char*)&BHSID.desc-(char*)&BHSID);
    stortabreq.append(HRSTORAGEALLOCATIONUNITS_BASE,INT_TAG,
		      (char*)&BHSID.allocunits-(char*)&BHSID);
    stortabreq.append(HRSTORAGESIZE_BASE,INT_TAG,
		      (char*)&BHSID.size-(char*)&BHSID);
    stortabreq.append(HRSTORAGEUSED_BASE,INT_TAG,
		      (char*)&BHSID.used-(char*)&BHSID);
    stortabreq.append(HRSTORAGEALLOCATIONFAILURES_BASE,COUNTER_TAG,
		      (char*)&BHSID.failures-(char*)&BHSID);
    try{
      assert(storage=(HrStorageTable*)stortabreq.get(len));
    } catch (SNMP_error_unrecoverable &error){
      handle_unrecoverable(progname, session.Hostname(),error);
    }

    while(len){
      len--;
      char buf[MAXSTR];
      snprintf(buf,MAXSTR,"desc=\"%s\";allocunits=\"%u\";size=\"%u\";"
	       "used=\"%u\";allocfail=\"%u\";\n",storage[len].desc,
	     storage[len].allocunits,storage[len].size,storage[len].used,
	     storage[len].failures);
      session.printstr(argflags,NEEDNL_ENTRY,buf);
    }
    delete storage;
  }
}

void hpcfgsrc(SNMP_session &session,unsigned long *argflags,
	      BerBase *cfgsrcdat){
  assert(cfgsrcdat->type()==INT_TAG);
  unsigned long cfgtype=((BerInt*)cfgsrcdat)->value();
  assert(cfgtype<=5 && HPCFGSOURCESTATES[cfgtype]!=NULL);
  char buf[MAXSTR];
  snprintf(buf,MAXSTR,"cfgsrc=\"%s\";",HPCFGSOURCESTATES[cfgtype]);
  session.printstr(argflags,NEEDNL_ENTRY,buf);
}


syntax highlighted by Code2HTML, v. 0.9.1