Solved

Uploading file programmatically behind a firewall

Posted on 2006-07-05
59
3,684 Views
Last Modified: 2013-11-16
I have a customer that is behind a firewall in an very restrictively administered net.

My customer CANNOT

1. use FTP
2. install software
3. run remote access software such as RealVNC, TightVNC or LogMeIn (www.logmein.com)

On the contrary, he CAN

1. upload data via web browser
2. run programs (directly copied, not installed)
3. run remote access sofware such as TeamViewer (www.teamviewer.com)

My problem is that I have to move some data programmatically from its location to a web server (and viceversa).

I found lot of ways of DOWNLOADING data TO his PC. Among them I successfully used libcurl (http://curl.haxx.se/libcurl/). See http://www.experts-exchange.com/Programming/Programming_Languages/C/Q_21854099.html for details about my experiments.

Unfortunately I'm unable to UPLOAD data from his PC to a web server. Knowing that he could upload data via web browser I managed to use the "HTTP post" feature of libcurl. But my experimental transfer software works from any PC but his.

One can give it a try by downloading my experimental exe from

http://www.sitibs.com/files/stefano/260606.zip.

It will post the included postit2.c example file to my server

http://www.sitibs.com/branches/robertofederici/pages/sections/postit2.c.

Before uploading it, you can remove old copies of postit2.c by running

http://www.sitibs.com/branches/robertofederici/pages/sections/remove.php.

So, I need to find a successful way of programmatically UPLOADING data from his PC to the web server. Whatever implementable will work for me.

Thanks for your help
0
Comment
Question by:s_federici
  • 27
  • 15
  • 11
  • +2
59 Comments
 
LVL 30

Expert Comment

by:ded9
ID: 17040826
no need of programming
 Download http://www.youngzsoft.net/ccproxy/

he will be able to use all blocked services

can upload file to www.rapidshare.de

Reps

0
 
LVL 5

Expert Comment

by:skaap2k
ID: 17040873
Another way to do this is to use a SSLVPN - over port 443, I know of a few devices which do this (since i work with them a lot)

Citrix Access Gateway
Netscaler

Of course this is probably the most expensive way of achieving this!

Rgds,
Rob
0
 

Author Comment

by:s_federici
ID: 17040939
If I correctly understood what ccproxy is, I cannot use it. Indeed the networK is very restrictively managed, and I'm NOT the manager. If I can make use of it even not being able to install programs on my customer's PC (or any other PC on his net) I will be happy to know how to do it.

What is SSLVPN? Should I install it on my web werver? Please, add (links to) detailed instructions/directions/tutorials.
0
 
LVL 5

Expert Comment

by:skaap2k
ID: 17040974
This might be of some help:

http://openvpn.net/

I have not used this, but it looks like it should have everything you need to do.. and the port number is flexible, once connected with this, you can tunnel any protocol you like over it, also, you could use SCP (secure copy) - it uses port 22 (ssh) ..

Regards,
Rob
0
 

Author Comment

by:s_federici
ID: 17041059
I went to the examples and, eg at http://openvpn.net/man.html#lbAV, it says that I have to install OpenVPN on the two PCs I would like to connect. That, as I said, I cannot. I cannot install anything on customer's PC. Despite this, the small teamviewerQS.exe could run without any problem on his PC.
0
 
LVL 24

Expert Comment

by:slyong
ID: 17041100
s_federici,

Do you have access to the Web server?  What Web server are you running?  The reason I ask is that you can use HTTP put.  However, HTTP needs a CGI to handle the file.
0
 

Author Comment

by:s_federici
ID: 17041135
I have some access to the webserver (Running Apache 2.0 and PHP 4.3.2 on Red Hat Enterprise Linux ES release 3). For example, I can install PHP applications on it. But I cannot manage Apache settings.

Will HTTP put be different from HTTP post? I tried with HTTP post as, after a lot of work, a managed how to have it working (see http://www.experts-exchange.com/Programming/Programming_Languages/C/Q_21854099.html). What do I need to make HTTP put work?
0
 
LVL 30

Expert Comment

by:ded9
ID: 17041161
0
 
LVL 24

Expert Comment

by:slyong
ID: 17041162
Can you put the code of your code here for the HTTP post receiving part?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17041189
There's some VBScript, yout client could run to upload the file vi HTTP at:

http://www.motobit.com/tips/detpg_post-binary-data-url/

I adopted this approach for a corporate client with similar constraints to yours.
0
 

Author Comment

by:s_federici
ID: 17041191
Here you are the code

<?php
$uploaddir = '/home/httpd/vhosts/sitibs.com/XXX/';
$uploadfile = $uploaddir . basename($_FILES['sendfile']['name']);
move_uploaded_file($_FILES['sendfile']['tmp_name'], $uploadfile);
?>

To ded9:

What is this http://www.publicproxyservers.com/page1.html? Where should I install it?
0
 

Author Comment

by:s_federici
ID: 17041240
To rstaveley:
I hoped I could do it without having to rely on the IE object. That would make my solution less general. But if this is the only working way... why wanting to be stubbornly general after all? My customer does have a Win PC. To apply you strategy I need an example of what "FormData" and "Boundary" can be.
0
 
LVL 30

Expert Comment

by:ded9
ID: 17041308
http://www.youngzsoft.net/ccproxy/
you nedd to install proxy  on server

just follow the instruction in this site
0
 

Author Comment

by:s_federici
ID: 17041345
I cannot make use of ccproxy as my server is running Linux.
0
 
LVL 30

Expert Comment

by:ded9
ID: 17041400
okay download the http://www.youngzsoft.net/ccproxy/

install it
copy the ccproxy exe file mail it to customer as  a standalone exe and then tell him to launch the exe file . After that hecan use any service .
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17041733
> To apply you strategy I need an example of what "FormData" and "Boundary" can be.

Here's the complete .vbs utility for file uploading.

--------8<--------

' See http://www.pstruh.cz/tips/detpg_uploadvbsie.htm

'Upload file using http protocol And multipart/form-data
'v1.01
'2001 Antonin Foller, PSTRUH Software
do_vbsUpload

Sub do_vbsUpload()
  'We need at least two arguments (File + URL)
  If WScript.Arguments.Count < 2 Then InfoEcho

  'Are some required objects missing?
  If InStr(CheckRequirements, "Error") > 0 Then InfoEcho

  Dim FileName, DestURL, FieldName
  FieldName = "FileField" 'Default field name

  Dim aCounter, Arg
  aCounter = 1 'Argument counter
  For Each Arg In WScript.Arguments
    Select Case aCounter
      Case 1: FileName = Arg
      Case 2: DestURL = Arg
      Case 3: FieldName = Arg
    End Select
    aCounter = aCounter + 1
  Next

  UploadFile DestURL, FileName, FieldName
End Sub



'******************* upload - begin
'Upload file using input type=file
Sub UploadFile(DestURL, FileName, FieldName)
  'Boundary of fields.
  'Be sure this string is Not In the source file
  Const Boundary = "---------------------------0123456789012"

  Dim FileContents, FormData
  'Get source file As a binary data.
  FileContents = GetFile(FileName)

  'Build multipart/form-data document
  FormData = BuildFormData(FileContents, Boundary, FileName, FieldName)

  Dim result

  'Post the data To the destination URL
  'IEPostBinaryRequest DestURL, FormData, Boundary
  result = WinHTTPPostRequest(DestURL, FormData, Boundary)

  WScript.Echo result

End Sub

'Build multipart/form-data document with file contents And header info
Function BuildFormData(FileContents, Boundary, FileName, FieldName)
  Dim FormData, Pre, Po
  Const ContentType = "application/upload"

  'The two parts around file contents In the multipart-form data.
  Pre = "--" + Boundary + vbCrLf + mpFields(FieldName, FileName, ContentType)
  Po = vbCrLf + "--" + Boundary + "--" + vbCrLf

  'Build form data using recordset binary field
  Const adLongVarBinary = 205
  Dim RS: Set RS = CreateObject("ADODB.Recordset")
  RS.Fields.Append "b", adLongVarBinary, Len(Pre) + LenB(FileContents) + Len(Po)
  RS.Open
  RS.AddNew
    Dim LenData
    'Convert Pre string value To a binary data
    LenData = Len(Pre)
    RS("b").AppendChunk (StringToMB(Pre) & ChrB(0))
    Pre = RS("b").GetChunk(LenData)
    RS("b") = ""

    'Convert Po string value To a binary data
    LenData = Len(Po)
    RS("b").AppendChunk (StringToMB(Po) & ChrB(0))
    Po = RS("b").GetChunk(LenData)
    RS("b") = ""

    'Join Pre + FileContents + Po binary data
    RS("b").AppendChunk (Pre)
    RS("b").AppendChunk (FileContents)
    RS("b").AppendChunk (Po)
  RS.Update
  FormData = RS("b")
  RS.Close
  BuildFormData = FormData
End Function

'Infrormations In form field header.
Function mpFields(FieldName, FileName, ContentType)
  Dim MPTemplate 'template For multipart header
  MPTemplate = "Content-Disposition: form-data; name=""{field}"";" + _
   " filename=""{file}""" + vbCrLf + _
   "Content-Type: {ct}" + vbCrLf + vbCrLf
  Dim Out
  Out = Replace(MPTemplate, "{field}", FieldName)
  Out = Replace(Out, "{file}", FileName)
  mpFields = Replace(Out, "{ct}", ContentType)
End Function


Sub Wait(Seconds, Message)
  On Error Resume Next
  CreateObject("wscript.shell").Popup Message, Seconds, "", 64
End Sub


'Returns file contents As a binary data
Function GetFile(FileName)
  Dim Stream: Set Stream = CreateObject("ADODB.Stream")
  Stream.Type = 1 'Binary
  Stream.Open
  Stream.LoadFromFile FileName
  GetFile = Stream.Read
  Stream.Close
End Function

'Converts OLE string To multibyte string
Function StringToMB(S)
  Dim I, B
  For I = 1 To Len(S)
    B = B & ChrB(Asc(Mid(S, I, 1)))
  Next
  StringToMB = B
End Function

'sends multipart/form-data To the URL using IE
Function IEPostBinaryRequest(URL, FormData, Boundary)
  'Create InternetExplorer
  Dim IE: Set IE = CreateObject("InternetExplorer.Application")

  'You can uncoment Next line To see form results
  'IE.Visible = True

  'Send the form data To URL As POST multipart/form-data request
  IE.Navigate URL, , , FormData, _
    "Content-Type: multipart/form-data; boundary=" + Boundary + vbCrLf

  Do While IE.Busy
    Wait 1, "Upload To " & URL
  Loop

  'Get a result of the script which has received upload
  On Error Resume Next
  IEPostBinaryRequest = IE.Document.body.innerHTML
  IE.Quit
End Function

'sends multipart/form-data To the URL using WinHttprequest/XMLHTTP
'FormData - binary (VT_UI1 | VT_ARRAY) multipart form data
Function WinHTTPPostRequest(URL, FormData, Boundary)
  Dim http 'As New MSXML2.XMLHTTP

  'Create XMLHTTP/ServerXMLHTTP/WinHttprequest object
  'You can use any of these three objects.
  Set http = CreateObject("WinHttp.WinHttprequest.5")
  'Set http = CreateObject("MSXML2.XMLHTTP")
  'Set http = CreateObject("MSXML2.ServerXMLHTTP")

  'Open URL As POST request
  http.Open "POST", URL, False

  'Set Content-Type header
  http.setRequestHeader "Content-Type", "multipart/form-data; boundary=" + Boundary

  'Send the form data To URL As POST binary request
  http.send FormData

  'Get a result of the script which has received upload
  WinHTTPPostRequest = http.responseText
End Function

'******************* upload - end

'******************* Support
'Basic script info
Sub InfoEcho()
  Dim Msg
  Msg = Msg + "Upload file using http And multipart/form-data" & vbCrLf
  Msg = Msg + "Copyright (C) 2001 Antonin Foller, PSTRUH Software" & vbCrLf
  Msg = Msg + "use" & vbCrLf
  Msg = Msg + "[cscript|wscript] fupload.vbs file url [fieldname]" & vbCrLf
  Msg = Msg + "  file ... Local file To upload" & vbCrLf
  Msg = Msg + "  url ... URL which can accept uploaded data" & vbCrLf
  Msg = Msg + "  fieldname ... Name of the source form field." & vbCrLf
  Msg = Msg + vbCrLf + CheckRequirements
  WScript.Echo Msg
  WScript.Quit
End Sub

'Checks If all of required objects are installed
Function CheckRequirements()
  Dim Msg
  Msg = "This script requires some objects installed To run properly." & vbCrLf
  Msg = Msg & CheckOneObject("ADODB.Recordset")
  Msg = Msg & CheckOneObject("ADODB.Stream")
  Msg = Msg & CheckOneObject("InternetExplorer.Application")
  Msg = Msg & CheckOneObject("WinHttp.WinHttprequest.5")
  CheckRequirements = Msg
'  MsgBox Msg
End Function

'Checks If the one object is installed.
Function CheckOneObject(oClass)
  Dim Msg
  On Error Resume Next
  CreateObject oClass
  If Err = 0 Then Msg = "OK" Else Msg = "Error:" & Err.Description
  CheckOneObject = oClass & " - " & Msg & vbCrLf
End Function
'******************* Support - end

--------8<--------
0
 

Author Comment

by:s_federici
ID: 17043489
To ded9:

unfortunately ccproxy doesn't have any effect (at least simply copied and launched)
0
 
LVL 24

Expert Comment

by:slyong
ID: 17046061
Could you try downloading the curl binary from http://curl.haxx.se/dlwiz/?type=bin&os=Win32&flav=-&ver=2000%2FXP
and use the command like followings to try uploading your file?

curl -F sendfile=@localfilename http://sitibs.com/XXX/yourphpreceivingscript.php
0
 

Author Comment

by:s_federici
ID: 17046667
what does the @localfilename syntax mean?
0
 
LVL 24

Expert Comment

by:slyong
ID: 17047382
=@localfilename
      ^^^^^^^^^^^^^ replace this with the filename you want to upload... for example:

curl -F sendfile=@postit2.c http://sitibs.com/XXX/yourphpreceivingscript.php
0
 

Author Comment

by:s_federici
ID: 17048586
As testing every suggestion at my customer's office takes some time to me, would you mind in the meatime explaining why do you feel that this is going to be different wrt running the (supposedly) same algorithm from inside libcurl?
0
 
LVL 24

Expert Comment

by:slyong
ID: 17048744
If you are referring to the program http://www.sitibs.com/branches/robertofederici/pages/sections/postit2.c.  It is http put which is a bit different to handle then http post.  On your PHP script for receiving, you do http post receiving.  But the postit2.c program you do http put method.  To understand how http post and http put works have a read at http://www.apacheweek.com/features/put
0
 
LVL 24

Expert Comment

by:slyong
ID: 17048753
You can of course write a program using libcurl to do http post, if curl works, you might want to then write out the program with libcurl.  I didn't have a machine to test the postit2.c program.  I don't use #include "stdafx.h" and #include <windows.h> that much.
0
 

Author Comment

by:s_federici
ID: 17049012
Sorry for the misleading file I used, postit2.c was a previous attempt to use libcurl to solve my problem. It does use http put. The application you can download from http://www.sitibs.com/files/stefano/260606.zip does use http post instead. And it works fine (from my PC and other PC without particular restrictions) except from my customer's PC. I tried the curl exe. It works fine too. But I guess that it won't work on my customer's PC (as http post of libcurl didn't work).
0
 
LVL 24

Expert Comment

by:slyong
ID: 17049087
I can't see what the application is doing.  The source code in there is only postit2.c, unless I can see the source code of the .EXE otherwise, I wouldn't know if there is a problem.
0
 

Author Comment

by:s_federici
ID: 17049114
To test the .EXE you can:

1. go to http://www.sitibs.com/branches/robertofederici/pages/sections/remove.php to remove (if any) old copies of postit2.c from the http://www.sitibs.com/branches/robertofederici/pages/sections/ folder
2. Try to download http://www.sitibs.com/branches/robertofederici/pages/sections/postit2.c (it should now fail)
3. run MyApplication.exe
3. Try to download http://www.sitibs.com/branches/robertofederici/pages/sections/postit2.c again (this time it should succeed and this whould show that libcurl http post does work at your end)
0
 
LVL 24

Expert Comment

by:slyong
ID: 17049211
Hi s_federici,

No offence but it would be better if you post your source code (removing whatever is sensitive).  It is not advisable to run an .EXE without knowing what is inside.  Also have you check if your client has got proxy setting in their browser?  You may need to set the proxy for libcurl / curl when you are at your client site.
0
 

Author Comment

by:s_federici
ID: 17049245
As I said, I have no administrative rights on my customer's PC and I cannot change its browser settings.

As for the .EXE, this code without sensible information:

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>

#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>

 
 int main(int argc, char *argv[])
 {
   CURL *curl;
   CURLcode res;
 
   struct curl_httppost *formpost=NULL;
   struct curl_httppost *lastptr=NULL;
   struct curl_slist *headerlist=NULL;
   char buf[] = "Expect:";
 
   curl_global_init(CURL_GLOBAL_ALL);
 
   /* Fill in the file upload field */
   curl_formadd(&formpost,
                &lastptr,
                CURLFORM_COPYNAME, "sendfile",
                CURLFORM_FILE, "postit2.c",
                CURLFORM_END);
 
   /* Fill in the filename field */
   curl_formadd(&formpost,
                &lastptr,
                CURLFORM_COPYNAME, "filename",
                CURLFORM_COPYCONTENTS, "postit2.c",
                CURLFORM_END);
 
 
   /* Fill in the submit field too, even if this is rarely needed */
   curl_formadd(&formpost,
                &lastptr,
                CURLFORM_COPYNAME, "submit",
                CURLFORM_COPYCONTENTS, "send",
                CURLFORM_END);
 
   curl = curl_easy_init();
   /* initalize custom header list (stating that Expect: 100-continue is not
      wanted */
   headerlist = curl_slist_append(headerlist, buf);
   if(curl) {
     /* what URL that receives this POST */
     curl_easy_setopt(curl, CURLOPT_URL, "http://www.sitibs.com/XXX/script-to-receive-post.php");
     if ( (argc == 2) && (!strcmp(argv[1], "noexpectheader")) )
       /* only disable 100-continue header if explicitly requested */
       curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
     curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
     res = curl_easy_perform(curl);
 
     /* always cleanup */
     curl_easy_cleanup(curl);
 
     /* then cleanup the formpost chain */
     curl_formfree(formpost);
     /* free slist */
     curl_slist_free_all (headerlist);
   }
   return 0;
 }


0
 
LVL 24

Expert Comment

by:slyong
ID: 17049262
Hi s_federici,

You don't need to change the proxy on your client machine.  Can you check if your client is going through a proxy?  If you cannot verify that, and your client is using a proxy, there is no way to make this work.
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 

Author Comment

by:s_federici
ID: 17049295
Is there a way of checking this other than asking him (that doesn't know that) or its net administrator (that is not feasible?)
0
 
LVL 24

Expert Comment

by:slyong
ID: 17049334
Cut and paste the following code into a file call checkProxy.vbs and have your client double click on the script and tell you the messages that are displayed on the screen.

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer _
    & "\root\cimv2\Applications\MicrosoftIE")
Set colIESettings = objWMIService.ExecQuery _
    ("Select * from MicrosoftIE_LANSettings")
For Each strIESetting in colIESettings
    Wscript.Echo "Autoconfiguration proxy: " & strIESetting.AutoConfigProxy
    Wscript.Echo "Autoconfiguration URL: " & strIESetting.AutoConfigURL
    Wscript.Echo "Autoconfiguration Proxy detection mode: " & _
        strIESetting.AutoProxyDetectMode
    Wscript.Echo "Proxy: " & strIESetting.Proxy
    Wscript.Echo "Proxy override: " & strIESetting.ProxyOverride
    Wscript.Echo "Proxy server: " & strIESetting.ProxyServer
Next
0
 
LVL 24

Expert Comment

by:slyong
ID: 17049410
btw only works with Internet Explorer..
0
 

Author Comment

by:s_federici
ID: 17054813
To rstaveley:

I run your vbs script and (on my PC running WinXP Pro not my customer's) I got the following error message:

winHTTP.WinHttprequest.5 - Error: ActiveX component cannot create object

Is there a way to overcome this?
0
 

Author Comment

by:s_federici
ID: 17056385
To rstavely:

winhttp.dll is present at C:\WINDOWS\system32.

At http://windowssdk.msdn.microsoft.com/en-us/library/ms736768.aspx (and other URLs) I found that

"If you get the error value, "ActiveX component can't create object," the WinHttp.dll was not properly registered or is not present on the system".

So, as it is present, why should it by not properly registered? Is this a common case (as I found a lot of messages on the net complaining for it)? If so, it is unlikely that I can use it to overcome my problems, as registering a dll is usually blocked when there are restrictions. Or is there an easy way to solve this?
0
 

Author Comment

by:s_federici
ID: 17056485
I tried with

regsvr32 "C:\windows\system32\winhttp.dll"

I still get the same error message.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17056510
It is really frustrating dealing with corporates, isn't it?

Did you try running regsvr32 to register the DLL manually?

Usage:

    regsvr32 C:\WINDOWS\system32\WinHttp.dll

(See http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/regsvr32.mspx)

Or have they prevented you from being able to use regsvr32 too?

Otherwise, I guess you would need to install MSXML4 - see http://www.microsoft.com/downloads/details.aspx?FamilyID=3144b72b-b4f2-46da-b4b6-c5d7485f2b42&DisplayLang=en but your client no doubt has imposed restrictions preventing this.

I take it your client is amenable to scripts, but isn't amenable to installing .exe etc?

NB: You would also need MSXML for the alternatives to WinHttp.WinHttprequest.5 (i.e. MSXML2.XMLHTTP and MSXML2.ServerXMLHTTP)
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17056511
[Sorry that was posted before your post.]
0
 

Author Comment

by:s_federici
ID: 17056585
I went to http://www.pstruh.cz/tips/detpg_uploadvbsie.htm (it redirected me to http://www.motobit.com/tips/detpg_uploadvbsie/). I noticed that the listed code didn't make use of winhttp. So I tried it, and it worked (at least on my PC). What is the difference in using the winhttprequest object?
0
 
LVL 17

Accepted Solution

by:
rstaveley earned 500 total points
ID: 17056637
When you use Function IEPostBinaryRequest(URL, FormData, Boundary) you are automating IE, which could be your best approach, bearing in mind that you cannot isr the WinHttp.WinHttprequest.5. If you look at the source for IEPostBinaryRequest, you'll see that it uses the ActiveX object InternetExplorer.Application, which is of course Internet Explorer.

Do you get any joy with...

--------8<--------
'Upload file using http protocol And multipart/form-data
'v1.01
'2001 Antonin Foller, PSTRUH Software
do_vbsUpload

Sub do_vbsUpload()
  'We need at least two arguments (File + URL)
  If WScript.Arguments.Count < 2 Then InfoEcho
 
  'Are some required objects missing?
  If InStr(CheckRequirements, "Error") > 0 Then InfoEcho
 
  Dim FileName, DestURL, FieldName
  FieldName = "FileField" 'Default field name
 
  Dim aCounter, Arg
  aCounter = 1 'Argument counter
  For Each Arg In WScript.Arguments
    Select Case aCounter
      Case 1: FileName = Arg
      Case 2: DestURL = Arg
      Case 3: FieldName = Arg
    End Select
    aCounter = aCounter + 1
  Next
 
  UploadFile DestURL, FileName, FieldName
End Sub



'******************* upload - begin
'Upload file using input type=file
Sub UploadFile(DestURL, FileName, FieldName)
  'Boundary of fields.
  'Be sure this string is Not In the source file
  Const Boundary = "---------------------------0123456789012"
 
  Dim FileContents, FormData
  'Get source file As a binary data.
  FileContents = GetFile(FileName)
 
  'Build multipart/form-data document
  FormData = BuildFormData(FileContents, Boundary, FileName, FieldName)
 
  'Post the data To the destination URL
  IEPostBinaryRequest DestURL, FormData, Boundary
End Sub

'Build multipart/form-data document with file contents And header info
Function BuildFormData(FileContents, Boundary, FileName, FieldName)
  Dim FormData, Pre, Po
  Const ContentType = "application/upload"
 
  'The two parts around file contents In the multipart-form data.
  Pre = "--" + Boundary + vbCrLf + mpFields(FieldName, FileName, ContentType)
  Po = vbCrLf + "--" + Boundary + "--" + vbCrLf
 
  'Build form data using recordset binary field
  Const adLongVarBinary = 205
  Dim RS: Set RS = CreateObject("ADODB.Recordset")
  RS.Fields.Append "b", adLongVarBinary, Len(Pre) + LenB(FileContents) + Len(Po)
  RS.Open
  RS.AddNew
    Dim LenData
    'Convert Pre string value To a binary data
    LenData = Len(Pre)
    RS("b").AppendChunk (StringToMB(Pre) & ChrB(0))
    Pre = RS("b").GetChunk(LenData)
    RS("b") = ""
   
    'Convert Po string value To a binary data
    LenData = Len(Po)
    RS("b").AppendChunk (StringToMB(Po) & ChrB(0))
    Po = RS("b").GetChunk(LenData)
    RS("b") = ""
   
    'Join Pre + FileContents + Po binary data
    RS("b").AppendChunk (Pre)
    RS("b").AppendChunk (FileContents)
    RS("b").AppendChunk (Po)
  RS.Update
  FormData = RS("b")
  RS.Close
  BuildFormData = FormData
End Function

'sends multipart/form-data To the URL using IE
Function IEPostBinaryRequest(URL, FormData, Boundary)
  'Create InternetExplorer
  Dim IE: Set IE = CreateObject("InternetExplorer.Application")
 
  'You can uncoment Next line To see form results
  'IE.Visible = True
   
  'Send the form data To URL As POST multipart/form-data request
  IE.Navigate URL, , , FormData, _
    "Content-Type: multipart/form-data; boundary=" + Boundary + vbCrLf

  Do While IE.Busy
    Wait 1, "Upload To " & URL
  Loop
 
  'Get a result of the script which has received upload
  On Error Resume Next
  IEPostBinaryRequest = IE.Document.body.innerHTML
  IE.Quit
End Function

'Infrormations In form field header.
Function mpFields(FieldName, FileName, ContentType)
  Dim MPTemplate 'template For multipart header
  MPTemplate = "Content-Disposition: form-data; name=""{field}"";" + _
   " filename=""{file}""" + vbCrLf + _
   "Content-Type: {ct}" + vbCrLf + vbCrLf
  Dim Out
  Out = Replace(MPTemplate, "{field}", FieldName)
  Out = Replace(Out, "{file}", FileName)
  mpFields = Replace(Out, "{ct}", ContentType)
End Function


Sub Wait(Seconds, Message)
  On Error Resume Next
  CreateObject("wscript.shell").Popup Message, Seconds, "", 64
End Sub


'Returns file contents As a binary data
Function GetFile(FileName)
  Dim Stream: Set Stream = CreateObject("ADODB.Stream")
  Stream.Type = 1 'Binary
  Stream.Open
  Stream.LoadFromFile FileName
  GetFile = Stream.Read
  Stream.Close
End Function

'Converts OLE string To multibyte string
Function StringToMB(S)
  Dim I, B
  For I = 1 To Len(S)
    B = B & ChrB(Asc(Mid(S, I, 1)))
  Next
  StringToMB = B
End Function
'******************* upload - end

'******************* Support
'Basic script info
Sub InfoEcho()
  Dim Msg
  Msg = Msg + "Upload file using http And multipart/form-data" & vbCrLf
  Msg = Msg + "Copyright (C) 2001 Antonin Foller, PSTRUH Software" & vbCrLf
  Msg = Msg + "use" & vbCrLf
  Msg = Msg + "[cscript|wscript] fupload.vbs file url [fieldname]" & vbCrLf
  Msg = Msg + "  file ... Local file To upload" & vbCrLf
  Msg = Msg + "  url ... URL which can accept uploaded data" & vbCrLf
  Msg = Msg + "  fieldname ... Name of the source form field." & vbCrLf
  Msg = Msg + vbCrLf + CheckRequirements
  WScript.Echo Msg
  WScript.Quit
End Sub

'Checks If all of required objects are installed
Function CheckRequirements()
  Dim Msg
  Msg = "This script requires some objects installed To run properly." & vbCrLf
  Msg = Msg & CheckOneObject("ADODB.Recordset")
  Msg = Msg & CheckOneObject("ADODB.Stream")
  Msg = Msg & CheckOneObject("InternetExplorer.Application")
  CheckRequirements = Msg
'  MsgBox Msg
End Function

'Checks If the one object is installed.
Function CheckOneObject(oClass)
  Dim Msg
  On Error Resume Next
  CreateObject oClass
  If Err = 0 Then Msg = "OK" Else Msg = "Error:" & Err.Description
  CheckOneObject = oClass & " - " & Msg & vbCrLf
End Function
'******************* Support - end
--------8<--------
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17056665
Alas, I can't remember why I avoided automating InternetExplorer.Application, when I did this myself in a similar set up to yours. I've got a vague idea that it didn't work for me possibly because of some security restriction that I didn't get to the bottom of, ...but this was all 4 years ago. Certainly have a go using the InternetExplorer.Application automation approach above, when you get access to your client's premises,
0
 

Author Comment

by:s_federici
ID: 17056901
I tried the script at my customer's office... it worked!

Now, is there a way to do the same from inside a program? certainly I could call the script from inside an exe by passing the right arguments, but I'm not completely sure that this is a safe solution.

Is this same code usable as a normal VB code? I'm not an expert VB developer. Can I directly copy it in VB and create an exe or dll? Or is it possible to move this code to C?
0
 

Author Comment

by:s_federici
ID: 17056993
Ok, I answered myself. I could compile it under VB with minimal modifications. This is great. One last thing that I don't know is if there is a way to know how much of the file has been transferred each time Wait is called. It would be indeed great if I could add a progressbar to my transfer program.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17057086
I plagiarised some VBScript written by J Warrington for a progress bar, when I did it, and re-wriote it as a Windows script component. The original code seems to be no longer available on the Internet, so here's my plagiarism, written in JScript, which I was more comfortable using than VBScript.

progress.wsc:
--------8<--------
<?xml version="1.0" ?>

<component>

<comment>

      Progress indicator script, using Internet Explorer

      Plagiarised from the VBScript component written by J Warrington
      See http://home.att.net/~wshvbs/wscProgressDialogPage.htm

      See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/letckerror.asp
      for information about .wsc components.

      Revisions:

      21-03-03 ie.quit put into a try...catch...

</comment>

<comment>

            Debug processing directive

      NB: comment this out in the production environment to ensure that the script
      runs silently.

</comment>
<?component error="true" debug="true" ?>

<comment>

      Class registration

      The COM component's clsid is a unique 128-bit ID
      generated by uuidgen.exe from the COM toolkit
      \TOOLs directory (or by calling CoCreateGuid)

      Use the following to register the component:
      regsvr32 file:\\myserver\simple.wsc
      Alternatively right-click in Windows Explorer
      and select "register".

</comment>
<registration
      description="Progress indicator"
      progid="com.seseit.util.progress.script.1"
      version="0.1"
      classid="{348867FB-F023-4453-8076-4B919E2054E1}"
      remotable="true" />

<comment>

      Script component type library used by script.

      This is required for Visual Basic

</comment>
<comment><reference object="com.seseit.util.progress.script.1" /></comment>

<comment>

      Interface definition.

      This exposes the methods in our script, which are called
      by the Calam application.

</comment>
<public>

      <comment>Open the progress indicator</comment>
      <method name="Open" internalname="OpenIE">
            <parameter name="title" />
            <parameter name="left" />
            <parameter name="top" />
            <parameter name="width" />
            <parameter name="height" />
      </method>

      <comment>Close the progress indicator</comment>
      <method name="Close" />

      <comment>Make the indicator visible/invisible</comment>
      <method name="Show" >
            <parameter name="visible" />
      </method>

      <property name="Title" put="SetTitle" />
      <property name="Text" put="SetText" />
      <property name="Percent" put="SetPercent" />
      <property name="Log" get="GetLog" />
      <property name="Button" put="SetButtonText" />

      <comment>Change the %age completion</comment>

      <event name="Cancel" />
      <event name="Close" />
      <event name="Log" />

</public>

<script language="VBScript" >
<![CDATA[

sub ReferenceVBSEventHandlers(o)
      o.all.Cancel.OnClick = GetRef("evtOnCancel")
      o.body.OnUnLoad = GetRef("evtOnUnload")
end sub

private sub evtOnCancel()
      FireEvent("Cancel")
end sub

private sub evtOnUnload()
      FireEvent("Close")
end sub

]]>
</script>

<script language="JScript" >
<![CDATA[

var ie = null;
var textPara = null;
var titlePara = null;
var progressInput = null;
var cancelButton = null;
var displayedBlocks = 0;

/* The "wingdings" "n" character looks like a little block (there are other
   possibilities, try "terminal" &#219, or, if you're feeling frivolous, try
   "smiley faces") */

var blockChar = "n";
var cancelled = false;
var logStr = null;

/******** log *************************************************************/
function log(str)
{
      logStr = str;
      fireEvent("Log");
}

/******** GetLog **********************************************************/
function GetLog()
{
      return logStr;
}

/******** OpenIE **********************************************************/
/*
 * Open Internet Explorer
 */
function OpenIE(title,x,y,w,h)
{

      try {
            //ie = CreateObject("InternetExplorer.Application");
            ie = new ActiveXObject("InternetExplorer.Application");

            ie.left = x;
            ie.top = y;
            ie.width = w;
            ie.height = h;
            ie.menubar = false;
            ie.toolbar = false;
            ie.statusbar = false;
            ie.navigate("about:blank");      // Load an empty page

            if (!Wait())
                  return false;

/* Reference the document */

            var document = ie.document;

            document.open();
            document.write(getResource("progressHTML"));
            document.close();

            if (!Wait())
                  return false;

/* Lose the Internet Explorer text from the title, but padding the title
   with blank characters */

            for (var i = 0;i < 100;i++)
                  title += "\xa0";

            document.title = title;

/* Establish connections */

            titlePara = document.all("TitlePara");
            textPara = document.all("TextPara");
            progressInput = document.all("ProgressInput");
            cancelButton = document.all("Cancel");

/* Define event handler connections */

            ReferenceVBSEventHandlers(document);

            titlePara.innerText = "";
            textPara.innerText = "";

/* Set progress bar parameters */

            displayedBlocks = 0;

      }
      catch (e) {
            log("Exception: "+e.description);
            return false;
      }

      return true;
}

/******** Wait ************************************************************/
/*
 * Wait for IE to get ready
 */
function Wait()
{
      var READYSTATE_COMPLETE = 4;      // from enum of tagREADYSTATE...
      try {
            while (ie.ReadyState != READYSTATE_COMPLETE)
                  ;
      }
      catch (e) {
            log("Error: Exception thrown while waiting for IE to get ready: "+e.description);
            return false;
      }
      return true;
}

/******** Close ***********************************************************/
function Close()
{
      titlePara = null;
      textPara = null;
      progressInput = null;

      try {
            ie.quit();
      }
      catch (e) {
      }
      cancelled = true;
}

/******** Show ************************************************************/
function Show(visible)
{
      ie.visible = visible;
      cancelled = false;
}

/******** SetTitle ********************************************************/
/*
 * Set the title
 */
function SetTitle(str)
{
      if (titlePara != null)
            try {
                  titlePara.innerHTML = str;
            }
            catch (e) {
            }
}

/******** SetButtonText ***************************************************/
function SetButtonText(str)
{
      if (cancelButton != null)
            try {
                  cancelButton.value = str;
            }
            catch (e) {
            }
}

/******** SetText *********************************************************/
/*
 * Set the title
 */
function SetText(str)
{
      if (textPara != null)
            try {
                  textPara.innerHTML = str;
            }
            catch (e) {
            }
}

/******** SetPercent ******************************************************/
function SetPercent(str)
{
      if (progressInput == null)
            return;

      var blocks = parseInt(str) / 3;
      if (blocks != displayedBlocks) {
            displayedBlocks = blocks;
            var str = "";
            while (blocks-- > 0)
                  str += blockChar;
            try {
                  progressInput.value = str;
            }
            catch (e) {
            }
      }
}

]]>
</script>

<resource id="progressHTML">
<![CDATA[
<HTML>
<HEAD><TITLE> Progress </TITLE>
<STYLE>
BODY { margin-left: 10px; margin-top: 10px; margin-right: 2px; margin-bottom: 2px; }
SPAN { color:navy; font-family: Verdana; font-size: 10pt; font-weight: bold; }
INPUT { color: Navy; background: Silver; font-family: Wingdings;
      font-size: 10pt; height: 20px; width: 340px; }
BUTTON { position: absolute; left: 175; top: 120; width: 120; height: 24; }
P.footnote { position: absolute; left: 270; top: 150; color: Black;
      font-family: Arial; font-size: 7pt; font-weight: Normal; font-style: Italic; }
</STYLE>
</HEAD>
<BODY scroll=no bgcolor="Silver" >
<SPAN id="">
      <P id="TitlePara"> &nbsp </P>
      <P id="TextPara"> &nbsp </P>
</SPAN>
<CENTER><INPUT type="text" id="ProgressInput" value="" ></CENTER>
<BUTTON ID="Cancel"> Cancel </BUTTON>
<P class="footnote"> No warranties etc. </P>
</BODY></HTML>
]]>
</resource>

</component>
--------8<--------

To invoke this, you need to instantiate the object.

Here's what you'd put into a WSF:

<object id="progress" classid="CLSID:348867FB-F023-4453-8076-4B919E2054E1" events="true" />

Then you need to call it as follows (illustrated using JScript syntax from my WSF):

(1) Open and show the dialogue box:

      if (!progress.Open("Image uploader "+VerStr,60,100,470,200)) {
WScript.Echo("Error: Unable to open progress indicator");
            return 1;
      }
      progress.Show(true);

(2) Set the title

      progress.Title = "Blahdiblah";

(3) Set progress text whenever applicable

      progress.Text = "Reformatting hard disk...";

(4) Set the transfer percentage as and when it changes (I had to transfer multiple files, so I did this between file transfers)

      progress.Percent = 25;

(5) Set the OK button when the transfer is complete

      progress.Button = " OK ";

(6) Close the dialogue box

      progress.Close();

I hope this is helpful to you. If you are only transferring one file, it won't be.
0
 

Author Comment

by:s_federici
ID: 17057120
To rstaveley:
I'm going to go trought your new code. In the meantime you deserve full points to be awarded to you. Just one last info before I dig your code (as I don't know much of jscript): can I invoke your code from a command line or else? Or is it something that must be on a web server?!?...
0
 

Author Comment

by:s_federici
ID: 17057128
Ok, I understood tha WSF is Windows Script File. So I guess I answered myself, didn't I?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17057169
Yes, WSF = Winfows script file.

Here's a demo for the progress bar, which comes as two parts test.wsf and jsfuncs.js.

test.wsf:
--------8<--------
<job>
<script language="JScript">

var cancelled = false;
var closed = false;

/******* main *************************************************************/
/*
 * Executable code starts here
 */
function main(argc,argv)
{

/* Parse the arguments */

      if (parse(argc,argv))
            return 1;

      if (!progress.Open("Test script",60,100,470,200)) {
WScript.Echo("Error: Unable to open progress indicator");
            return 1;
      }

      progress.Show(true);
      progress.Title = "Progress test";

      var complete = 20;
      for (var i = 0;i < complete;i++) {

            if (cancelled)
                  break;

            var percent = ((i+1)*100)/complete;
            progress.Text = "Completion is "+percent+"%";
            progress.Percent = percent;
            WScript.Sleep(500);
      }

      if (!closed)
            progress.Close();

      return 0;
}

/******** parse ***********************************************************/
/*
 * Parse command line parameters
 *
 * Return 1 to prevent continuation in the calling code
 */
function parse(argc,argv)
{

      if (argc <= 0)
            return 0;

/* Run through the command line parameters */

      for (var index = 0;argc--;index++) {
            var parm = argv(index);
            isSwitch = (parm.substr(0,1) == "/");
            if (isSwitch) {

/* Usage */

                  if (parm.substr(1,1) == "?") {
WScript.Echo("Usage: "+WScript.ScriptName);
                        return 1;
                  }

                  continue;
            }
      }

      return 0;
}

function Cancel()
{
      cancelled = true;
}

function Close()
{
      closed = true;
}

</script>

<script language="VBScript">

' Event handler
sub progress_Log
      WScript.Echo progress.Log
end sub

' Event handler
sub progress_Cancel
      WScript.Echo "Cancelled"
      call Cancel()
end sub

' Event handler
sub progress_Close
      WScript.Echo "Closed"
      call Close()
      call Cancel()
end sub

</script>
<script language="JScript" src="jsfuncs.js" />
<object id="progress" classid="CLSID:348867FB-F023-4453-8076-4B919E2054E1" events="true" />
</job>
--------8<--------


jsfuncs.js
--------8<--------
/*
 * General purpose JScript functions
 */

/******* startup **********************************************************/
/*
 * Pick up the command line parameters and launch the main() function
 *
 * This is used as a start up stub for .wsf files
 */

/* Use interactive mode */

      WScript.Interactive = true;

/* WSHShell used for pop-up messages */

      //var WSHShell = WScript.CreateObject("WScript.Shell");

/* Pick up the arguments */

      argv = WScript.Arguments;
      retval = main(argv.length,argv);

/* Return to the OS with the return value from main. */

      WScript.Quit(retval);

/******* DisplayErrorText *************************************************/
/*
 * This function is required by calfuncs.vbs, which may be used in an
 * HTML file as well as a WScript file and therefore does not have an
 * explicit mechanism for displaying error text.
 */
function DisplayErrorText(msg)
{
      WScript.echo(msg);
}
--------8<--------

You will need to register the .wsc to use this and be wrned that the progress bar will not look good with IE7, because it assumes the dimensions of IE 6 or 5. If you have IE 7, you'll need to adjust the size used.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17057186
You'll notice from the style that my backgound in C/C++. Hence the use of main, with argc/argv.

It should be easy to port into VBScript; you'll notice that the event handling is already in VBScript rather than JScript.

The main difference between the two is that JScript gets the IE object using new ActiveXObject("InternetExplorer.Application") and VBScript uses CreateObject("InternetExplorer.Application").
0
 

Author Comment

by:s_federici
ID: 17057321
rstaveley, yes, I would need the progress bar to transfer a single document. I guess I have to do with a false progress bar giving my customer the impression that something is going on, haven't I?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17057409
Yes, alas. Unless you wanted to have something poll the received file size, but that could be a temporary file anyhow.
0
 

Author Comment

by:s_federici
ID: 17057437
> Unless you wanted to have something poll the received file size, but that could be a temporary file anyhow

I need more details on this.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17057524
You'd need a separate thread to run in Visual Basic and watch the file grow as it is downloaded in that thread. You'd use a FileSystemObject (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/8b99eead-e2bd-45c6-9660-bbbfeec192f0.asp) to watch the file size grow.

e.g.

showsize.vbs
--------8<--------
Function ShowFolderSize(filespec)

   Dim fso, f, s

   Set fso = CreateObject("Scripting.FileSystemObject")

   Set f = fso.GetFolder(filespec)

   s = UCase(f.Name) & " uses " & f.size & " bytes."

   ShowFolderSize = s

End Function

Function ShowFileSize(filespec)

   Dim fso, f, s

   Set fso = CreateObject("Scripting.FileSystemObject")

   Set f = fso.GetFile(filespec)

   s = UCase(f.Name) & " uses " & f.size & " bytes."

   ShowFileSize = s

End Function

WScript.Echo ShowFileSize("c:\x.txt")
--------8<--------
0
 

Author Comment

by:s_federici
ID: 17057544
Am I completely wrong or this is for a file download? Can I use a similar strategy for my upload?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17057706
LOL! Sorry, s_federici, you are absolutely right.

You'd need to poll something on the server that read the received file size
0
 

Author Comment

by:s_federici
ID: 17057801
> You'd need to poll something on the server that read the received file size

If I correctly understand how HTTP file transfer works, I will be able to check the transferred file only after it has been completely transferred... or not? Maybe (I'm just brainstorming with myself) I could split the file and then transfer each file chunk, then recompose the file on the server. But I guess this will add a considerable overhead.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17058079
They would be in separate threads if you use a separate instance of 'Internet.Explorer' to GET the filesize.

However...

> I could split the file and then transfer each file chunk, then recompose the file on the server. But I guess this will add a considerable overhead.

That's a good idea in my opinion. If it is a big enough file that you'd get to see the progress meter do its stuff, the additional overhead should be negligible.
0
 

Author Comment

by:s_federici
ID: 17058451
> They would be in separate threads if you use a separate instance of 'Internet.Explorer' to GET the filesize

This is unclear to me
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17058653
You would have one visible Internet.Explorer window for the progress bar, one invisible Internet.Explorer window for the file upload and another separate thread instantiating invisible Internet.Explorer instances to GET the current file size from the server. It would be pretty ugly. I'd go for the file splitting.
0
 

Author Comment

by:s_federici
ID: 17058867
As it does interest me a lot, please excuse me if I keep bothering you after you already asnwered the topic of this question:

> separate thread instantiating invisible Internet.Explorer instances to GET the current file size from the server

Do you think that I could GET the file size of a document that is currently being transferred before the file has been completely transferred? If so, I WANT THE CODE :-)
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 17059105
The file is going to be downloaded into a temp directory and it will be the only one that's growing, presumably. I don't have the code, but if it was me I'd watch the upload directory (or temp directory) while the file is being uploaded.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Cisco MRA Phones 4 28
LAN or WAN ? 11 66
Remotely accessing Raspberry Pi from internet 4 62
Route summarization 9 45
What’s a web proxy server? A proxy server is a server that goes between clients and web servers, used in corporate to enforce corporate browsing policy and ensure security. Proxy servers are commonly used in three modes. A)    Forward proxy …
Data center, now-a-days, is referred as the home of all the advanced technologies. In-fact, most of the businesses are now establishing their entire organizational structure around the IT capabilities.
After creating this article (http://www.experts-exchange.com/articles/23699/Setup-Mikrotik-routers-with-OSPF.html), I decided to make a video (no audio) to show you how to configure the routers and run some trace routes and pings between the 7 sites…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now