unit Umain_hlp;

interface
uses windows, classes, sysutils, httpsend2, synautil, dialogs;

const
  CRLF = #13#10;
  MAX_FILE_SIZE = 4.9*1024*1024;

type
  Tlinktype = (ltimage, ltflash);
  Tuploadstate = (usready, usstarted, ushttperror, userror, ussuccess);

  Pcalldata = ^Tcalldata;
  Tcalldata = record
    idx,total:integer;
    filename:array[0..2048] of char;
    optsize:array[0..2048] of char;
    rembar:boolean;

    size:integer;
    uploaded:integer;
    speed:extended;
    userabort:boolean;
    errmsg:array[0..2048] of char;
    state:Tuploadstate;

    result_width,result_height:array[0..2048] of char;
    result_image_link:array[0..2048] of char;
    result_image_html:array[0..2048] of char;
    result_thumb_link,result_thumb_html:array[0..2048] of char;
    result_ad_link:array[0..2048] of char;
  end;

  Tuploadcallback = procedure(calldata:Pcalldata); cdecl;

  Timageshack = class
  private
    http:thttpsend2;
    strlist:tstringlist;

    Bound:string;
    poststream,poststream2:tmemorystream;

    function getmimetype(filename:string):string;
    procedure HTTPOnPostManual(Sender:TObject; var userabort:boolean);
  public
    calldata:Pcalldata;
    uploadcallback:Tuploadcallback;
    procedure upload;
    constructor Create;
    destructor Destroy; override;
    procedure stop;
  end;

implementation
uses Ufunction, Uimgsize;

constructor Timageshack.Create;
begin
  poststream:=tmemorystream.Create;
  poststream2:=tmemorystream.Create;
  http:=thttpsend2.Create;
  strlist:=tstringlist.Create;
end;

destructor Timageshack.Destroy;
begin
  poststream.Free;
  poststream2.Free;
  http.Free;
  strlist.Free;
end;

procedure Timageshack.stop;
begin
  http.Sock.CloseSocket;
end;

function issupportimage(s:string):boolean;
const
 ImageExt: array[0..6] of string = ('.jpeg','.jpg','.png',
  '.gif','.tiff','.tif','.bmp');
var
  i:integer;
begin
  result:=false;
  s:=lowercase(sysutils.ExtractFileExt(s));
  for i:=0 to high(ImageExt) do
    if s=ImageExt[i] then begin
      result:=true;
      break;
    end;
end;

procedure Timageshack.upload;
  procedure MakeMultipart(Bound,FieldName,FieldValue:string);
  var
    s:string;
  begin
    s := '--' + Bound + CRLF;
    s := s + 'Content-Disposition: form-data; name="' + FieldName + '"' + CRLF + CRLF;
    s := s + FieldValue + CRLF;
    WriteStrToStream(poststream, s);
  end;

var
  url,postmsg,s:string;
  postsize:integer;
  action:string;
  p1:integer;
  bound,s1:string;
begin
try
  if sysutils.FileExists(calldata.filename)=false then begin
    calldata.errmsg:='파일이 없습니다.';
    exit;
  end;
  if issupportimage(calldata.filename)=false then begin
    calldata.errmsg:='지원되는 파일 확장자가 아닙니다.';
    exit;
  end;

  //post
  http.Clear;
  Bound := IntToHex(Random(MaxInt), 8) + '_xxxxx_boundary';

  /////////////poststream
  poststream.Clear;
  MakeMultipart(Bound,'public','yes');
  MakeMultipart(Bound,'optimage','1');
  MakeMultipart(Bound,'optsize',calldata.optsize);
  if calldata.rembar then
    MakeMultipart(Bound,'rembar','1');
  MakeMultipart(Bound,'key','BXT1Z35V8f6ee0522939d8d7852dbe67b1eb9595');

  s := '--' + Bound + CRLF;
  s := s + 'Content-Disposition: form-data; name="fileupload";';
  s := s + ' filename="' + trim(calldata.filename) +'"' + CRLF;

  s := s + 'Content-Type: '+ getmimetype(calldata.filename) + CRLF + CRLF;
  WriteStrToStream(poststream, s);
  ////////////////////////////////

  /////////////poststream2
  poststream2.Clear;
  s:=CRLF;
  s:=s+'--' + Bound + '--' + CRLF;
  WriteStrToStream(poststream2, s);
  ////////////////////////////////

  //추가
  calldata.size:=Get_File_Size2(trim(calldata.filename));
  if calldata.size>MAX_FILE_SIZE then begin
    s1:=format('파일크기가 너무 큽니다. 업로드 최대가능크기는 %s 입니다.',[ConvertSize(round(MAX_FILE_SIZE))]);
    strpcopy(calldata.errmsg,s1);
    exit;
  end;

  postsize:=poststream.Size+poststream2.Size+Get_File_Size2(trim(calldata.filename));

  HTTP.Fpostmanual:=true;
  HTTP.Fpostsize:=postsize;
  HTTP.OnPostManual:=HTTPOnPostManual;

  //업로드
  url:='http://www.imageshack.us/upload_api.php';
  HTTP.MimeType := 'multipart/form-data; boundary=' + Bound;
  HTTP.HTTPMethod('POST', url);
  strlist.LoadFromStream(HTTP.Document);

  if calldata.userabort then
   exit;
{
  showmessage(HTTP.Headers.Text);
  form1.Memo1.Lines.Assign(strlist);
  exit;}
  //파싱
  s:=strlist.Text;
  if (http.ResultCode<>200) or (http.Sock.LastError<>0) or (s='') then begin
    calldata.state:=ushttperror;
    strpcopy(calldata.errmsg,format('code:%d(%s), sockerror:%d',[http.ResultCode,http.ResultString,http.Sock.LastError]));
    exit;
  end;
  p1:=pos('<error',lowercase(s));
  if p1>0 then begin
    delete(s,1,p1+1);
    strpcopy(calldata.errmsg,getvalue(s,'>','</error>'));
    calldata.state:=userror;
  end else begin
    strpcopy(calldata.result_width,getvalue(s,'<width>','</width>'));
    strpcopy(calldata.result_height,getvalue(s,'<height>','</height>'));
    strpcopy(calldata.result_image_link,speialcharhtml(getvalue(s,'<image_link>','</image_link>')));
    strpcopy(calldata.result_image_html,speialcharhtml(getvalue(s,'<image_html>','</image_html>')));
    strpcopy(calldata.result_thumb_link,speialcharhtml(getvalue(s,'<thumb_link>','</thumb_link>')));
    strpcopy(calldata.result_thumb_html,speialcharhtml(getvalue(s,'<thumb_html>','</thumb_html>')));
    strpcopy(calldata.result_ad_link,speialcharhtml(getvalue(s,'<ad_link>','</ad_link>')));
    calldata.state:=ussuccess;
  end;
except
  on E: Exception do begin
    calldata.state:=userror;
    strpcopy(calldata.errmsg,E.Message);
  end;
end;
end;

procedure Timageshack.HTTPOnPostManual(Sender:TObject; var userabort:boolean);
type
  TByte = array of byte;
var
  fs:TFilestream;
  buffer:TByte;
  e_count:integer;
  t,t1:integer;
  u:integer;
  s:string;
begin
  http.Sock.SendBuffer(poststream.Memory, poststream.Size);

  //send file
  calldata.uploaded:=0;
  t:=gettickcount;
  u:=0;
  fs:=TFilestream.Create(trim(calldata.filename), fmOpenRead or fmShareDenyNone);
  try
    fs.Position:=0;
    setlength(buffer,4096);
    while (fs.Position<fs.Size) do begin
      if calldata.userabort then begin
        calldata.errmsg:='취소 되었습니다.';
        userabort:=true;
        exit;
      end;
      e_count:=length(buffer);
      if fs.Position+e_count>fs.Size then e_count:=fs.Size-fs.Position;
      fs.ReadBuffer(buffer[0],e_count);
      http.Sock.SendBuffer(buffer,e_count);
      calldata.uploaded:=calldata.uploaded+e_count;
      t1:=gettickcount-t;
      if t1>2000 then begin
        t:=gettickcount;
        calldata.speed:=(calldata.uploaded-u) / (t1 / 1000);
        calldata.speed:=calldata.speed / 1000; //kbyte
        u:=calldata.uploaded;
      end;
      if assigned(uploadcallback) then
        uploadcallback(calldata);
    end;
  finally
    fs.Free;
  end;

  //send last
  http.Sock.SendBuffer(poststream2.Memory, poststream2.Size);
end;

function loadfromstream(stream:tstream):string;
var
  w,h:word;
  w2,h2:integer;
begin
  result:='';
   try
    GetStreamJPGSize(stream,w,h);
    if (w=0) and (h=0) then begin
      GetStreamGIFSize(stream,w,h);
      if (w=0) and (h=0) then begin
        GetStreamPNGSize(stream,w,h);
        if (w=0) and (h=0) then begin
          GetStreamBMPSize(stream,w2,h2);
          if (w2=0) and (h2=0) then begin
          end else begin
            result:='image/bmp';
          end;
        end else begin
          result:='image/png';
        end;
      end else begin
        result:='image/gif';
      end;
    end else begin
      result:='image/jpeg';
    end;
   except
   end;
  if result='' then begin
    result:='image/jpeg';
  end;
end;

function Timageshack.getmimetype(filename:string):string;
var
  stream:Tfilestream;
begin
  result:='image/jpeg';
  if sysutils.FileExists(filename)=false then
    exit;
  stream:=Tfilestream.Create(filename, fmopenread);
  try
    result:=loadfromstream(stream);
  finally
    stream.Free;
  end;
end;

end.
