C# Client for LS RemoteControl2

More
4 years 7 months ago - 4 years 6 months ago #117564 by floho
Hey,

I try to develop a c# client which is able to import a new survey into LS.

I read your manual on manual.limesurvey.org/RemoteControl_2_API but receive always this error message:


Bad Request
The CSRF token could not be verified.

The request could not be understood by the server due to malformed syntax. Please do not repeat the request without modifications.

If you think this is a server error, please contact the webmaster.
2015-02-23 18:33:27 Microsoft-IIS/7.5 Yii Framework/1.1.15


What do I have?
  • LS Version 2.05+ Build 141109
  • IIS 7.5 with PHP 5.4.x
  • MS SQL 2012 Database
RC2 settings:
RPC-Interface aktiviert: JSON-RPC
URL:http://xxx.de/limesurvey/index.php?r=admin/remotecontrol
API veröffentlichen unter /admin/remotecontrol: yes

What have I done?
I tried to connect like the examples on the manual page. At first I have installed RESTClient (mozilla plugin) and entered all the options. see attachment.
I also tried different URLs like:
xxx.de/limesurvey/index.php?r=admin/remotecontrol
xxx.de/limesurvey/index.php/admin/remotecontrol
xxx.de/limesurvey/admin/remotecontrol
-> didnt work.

I am not sure what I do wrong and what I should do.

Can someone help me please?

Thank you very much.
Attachments:
Last edit: 4 years 6 months ago by c_schmitz.

Please Log in or Create an account to join the conversation.

LimeSurvey Partners
More
4 years 6 months ago #117582 by floho
no idea ?

Please Log in or Create an account to join the conversation.

More
4 years 6 months ago #117587 by DenisChenu
Strange, what is your version and the build number ?

Did you update your config file ?

Can you control your internal.php file (in application/config/ directory).
'noCsrfValidationRoutes'=>array(
//              '^services/wsdl.*$'   // Set here additional regex rules for routes not to be validate
                'remotecontrol'
            ),

Assistance on LimeSurvey forum and LimeSurvey core development are on my free time.
I'm not a LimeSurvey GmbH member, professional service on demand , plugin development .
An error happen ? Before make a new topic : remind the Debug mode .

Please Log in or Create an account to join the conversation.

More
4 years 6 months ago #117605 by floho
Hey,

I am not sure what you exactly mean.

config.php
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
| -------------------------------------------------------------------
| This file will contain the settings needed to access your database.
|
| For complete instructions please consult the 'Database Connection'
| page of the User Guide.
|
| -------------------------------------------------------------------
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
|    'connectionString' Hostname, database, port and database type for 
|     the connection. Driver example: mysql. Currently supported:
|                 mysql, pgsql, mssql, sqlite, oci
|    'username' The username used to connect to the database
|    'password' The password used to connect to the database
|    'tablePrefix' You can add an optional prefix, which will be added
|                 to the table name when using the Active Record class
|
*/
return array(
	'components' => array(
		'db' => array(
			'connectionString' => 'sqlsrv:Server=localhost;Database=limesurvey',
			'username' => 'limesurvey',
			'password' => 'Start001',
			'charset' => 'utf8',
			'tablePrefix' => 'lime_',
			'initSQLs'=>array('SET DATEFORMAT ymd;','SET QUOTED_IDENTIFIER ON;'),
		),
 
		// Uncomment the following line if you need table-based sessions
		// 'session' => array (
			// 'class' => 'system.web.CDbHttpSession',
			// 'connectionID' => 'db',
			// 'sessionTableName' => '{{sessions}}',
		// ),
 
		'urlManager' => array(
			'urlFormat' => 'get',
			'rules' => require('routes.php'),
			'showScriptName' => true,
		),
 
	),
	// Use the following config variable to set modified optional settings copied from config-defaults.php
	'config'=>array(
	// debug: Set this to 1 if you are looking for errors. If you still get no errors after enabling this
	// then please check your error-logs - either in your hosting provider admin panel or in some /logs directory
	// on your webspace.
	// LimeSurvey developers: Set this to 2 to additionally display STRICT PHP error messages and get full access to standard templates
		'debug'=>2,
		'debugsql'=>0 // Set this to 1 to enanble sql logging, only active when debug = 2
	)
);
/* End of file config.php */
/* Location: ./application/config/config.php */

internal.php
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
 
/**
 * This file contains configuration parameters for the Yii framework.
 * Do not change these unless you know what you are doing.
 * 
 */
@date_default_timezone_set(@date_default_timezone_get());
$internalConfig = array(
	'basePath' => dirname(dirname(__FILE__)),
	'runtimePath' => dirname(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'tmp'.DIRECTORY_SEPARATOR.'runtime',
	'name' => 'LimeSurvey',
	'defaultController' => 'surveys',
	'import' => array(
		'application.core.*',
		'application.models.*',
		'application.controllers.*',
		'application.modules.*',
	),
	'components' => array(
        'bootstrap' => array(
            'class' => 'application.core.LSBootstrap',
            'responsiveCss' => false,
            'jqueryCss' => false
        ),
		'urlManager' => array(
			'urlFormat' => 'get',
			'rules' => require('routes.php'),
			'showScriptName' => true,
		),
 
        'clientScript' => array(
            'packages' => require('third_party.php')
        ),
        'assetManager' => array(
            'baseUrl' => '/tmp/assets'
        ),
		'request' => array(
            'class'=>'LSHttpRequest',
            'noCsrfValidationRoutes'=>array(
//              '^services/wsdl.*$'   // Set here additional regex rules for routes not to be validate
                'remotecontrol'
            ),
            'enableCsrfValidation'=>true,    // CSRF protection
            'enableCookieValidation'=>false   // Enable to activate cookie protection
        ),
        'user' => array(
            'class' => 'LSWebUser',
        ),
		'log' => array(
			'class' => 'CLogRouter'
		),
        'cache'=>array(
           'class'=>'system.caching.CFileCache',
        ),
        'db' => array(
                'schemaCachingDuration' => 3600,
        )
	)
);
 
if (!file_exists(dirname(__FILE__) .  '/config.php')) {
    $userConfig = require(dirname(__FILE__) . '/config-sample-mysql.php');
} else {
    $userConfig = require(dirname(__FILE__) . '/config.php');
}
 
$result = CMap::mergeArray($internalConfig, $userConfig);
/**
 * Some workarounds for erroneous settings in user config.php.
 */
$result['defaultController'] = $internalConfig['defaultController'];
return $result;
/* End of file internal.php */
/* Location: ./application/config/internal.php */

Thanks for your reply.

Please Log in or Create an account to join the conversation.

More
4 years 6 months ago #117612 by DenisChenu
RESTClient don't do application/json content (allways adding utf8 here). It's not the best tool to test.

The CRSF token seems to set if you sengd $_GET request and not $_POST.

Denis

Assistance on LimeSurvey forum and LimeSurvey core development are on my free time.
I'm not a LimeSurvey GmbH member, professional service on demand , plugin development .
An error happen ? Before make a new topic : remind the Debug mode .

Please Log in or Create an account to join the conversation.

More
4 years 6 months ago - 4 years 6 months ago #117615 by floho
I set the content-type to application/json and also to application/json-rpc but it always fails (screenshot). I tested an own written rest webservice with RESTClient (application/json) and it works fine.
So I dont know why it should not work with your service?

When i send with $_GET I receive only the plain html website (Header 200-OK and html content). With "enableCsrfValidation'=>false" I receive nothing - either plain html nor Header 200-OK.
Last edit: 4 years 6 months ago by floho.

Please Log in or Create an account to join the conversation.

More
4 years 6 months ago - 4 years 6 months ago #117678 by floho
I tried it now with a simple C# application. But this isnt working, too.
 public class Program {
 
    public static void Main(string[] args) {
 
      string Baseurl = "http://localhost/limesurvey/index.php/admin/remotecontrol";     
      string postData = "{'method':'get_session_key','params':{'username':'admin','password':'Start001'},'id':1}";
      byte[] byteArray = Encoding.UTF8.GetBytes(postData);
 
      Console.WriteLine(Send(Baseurl, "POST", postData));
      Console.Read();
      }
 
    public static string Send(string url, string Method, string content2Post) {
 
      string sPostData = content2Post;
      HttpWebRequest request = null;
      Uri URI = null;
 
      if (Method == "POST") {
 
        URI = new Uri(url);
 
        request = (HttpWebRequest)WebRequest.Create(URI);
        request.Method = Method; // "POST";
        request.ContentType = "application/json";
        request.ContentLength = sPostData.Length;
 
        Stream writeStream = request.GetRequestStream();
        System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
        byte[] bytes = encoding.GetBytes(sPostData);
        writeStream.Write(bytes, 0, bytes.Length);
        writeStream.Close();
      }
      else {
        URI = new Uri(string.Concat(url, "?", sPostData));
        request = (HttpWebRequest)WebRequest.Create(URI);
        request.Method = Method;
        request.ContentType = "application/json";
        request.ContentLength = 0;
      }
 
      HttpWebResponse response = (HttpWebResponse)request.GetResponse();
 
      //Überprüft den HTTP-Statuscode
      if (response.StatusCode != HttpStatusCode.OK)
        return "N,-1, HTTPStatuscode:" + response.StatusCode.ToString();
 
      Stream responseStream = response.GetResponseStream();
      StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8);
      return readStream.ReadToEnd();
 
    }
 
}

With GET i receive the plain html website. With POST I get an error. Sorry only in german:
Der Remoteserver hat einen Fehler zurückgegeben: (400) Ungültige Anforderung.

Is this now a bug in LS or from me? I have no idea what I do wrong..
Last edit: 4 years 6 months ago by floho.

Please Log in or Create an account to join the conversation.

More
4 years 6 months ago - 4 years 6 months ago #117690 by floho
Hey,

found the solution.. the url is xxx.de/limesurvey/index.php?r=admin/remotecontrol not xxx.de/limesurvey/index.php/admin/remotecontrol

I have written a small json-rpc client in c#. It is a small one but enough for my purposes. You can use it in your wiki as example or maybe it helps here other people.
It is tested and works in my environment but it is possible that i havent found all bugs.

You need from nuget package manager Newtonsoft JSON.

RPCclient.cs
using System;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
 
namespace JsonRPCclient {
 
  public class JsonRPCclient {
 
    private int id = 0;
    /// <summary>
    /// Set JSON-RPC webservice URL
    /// </summary>
    public string URL { set; get; }
    /// <summary>
    /// Set JSON-RPC method
    /// </summary>
    public string Method { set; get; }
    /// <summary>
    /// Add JSON-RPC params
    /// </summary>
    public JObject Parameters { set; get; }
 
    /// <summary>
    /// Results of the request
    /// </summary>
    public JsonRPCresponse Response { set; get; }
 
 
    /// <summary>
    /// Create a new object of RPCclient 
    /// </summary>
    public JsonRPCclient() {
      Parameters = new JObject();
      Response = null;
    }
 
    /// <summary>
    /// Create a new object of RPCclient
    /// </summary>
    /// <param name="URL"></param>
    public JsonRPCclient(string URL) {
      this.URL = URL;
      Parameters = new JObject();
      Response = null;
    }
 
    /// <summary>
    /// POST the request and returns server response
    /// </summary>
    /// <returns></returns>
    public string Post() {
      try {
        JObject jobject = new JObject();
        jobject.Add(new JProperty("jsonrpc", "2.0"));
        jobject.Add(new JProperty("id", ++id));
        jobject.Add(new JProperty("method", Method));
        jobject.Add(new JProperty("params", Parameters));
 
        string PostData = JsonConvert.SerializeObject(jobject);
        UTF8Encoding encoding = new UTF8Encoding();
        byte[] bytes = encoding.GetBytes(PostData);
 
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
        request.Method = "POST";
        request.ContentType = "application/json";
        request.KeepAlive = true;
        request.ContentLength = bytes.Length;
 
        Stream writeStream = request.GetRequestStream();
        writeStream.Write(bytes, 0, bytes.Length);
        writeStream.Close();
 
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream responseStream = response.GetResponseStream();
        StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8);
 
        Response = new JsonRPCresponse();
        Response = JsonConvert.DeserializeObject<JsonRPCresponse>(readStream.ReadToEnd());
        Response.StatusCode = response.StatusCode;
 
        return Response.ToString();
      }
      catch (Exception e) {
        return e.ToString();
      }
    }
 
    public void ClearParameters() {
      this.Parameters = new JObject();
    }
 
 
  }
 
  public class JsonRPCresponse {
    public int id { set; get; }
    public object result { set; get; }
    public string error { set; get; }
    public HttpStatusCode StatusCode { set; get; }
 
    public JsonRPCresponse() { }
 
    public override string ToString() {
      return "{\"id\":" + id.ToString() + ",\"result\":\"" + result.ToString() + "\",\"error\":" + error + ((String.IsNullOrEmpty(error)) ? "null" : "\"" + error + "\"") + "}";
    }
  }
 
}

programm.cs
      string Baseurl = "http://localhost/limesurvey/index.php?r=admin/remotecontrol";
      JsonRPCclient client = new JsonRPCclient(Baseurl);
      client.Method = "get_session_key";
      client.Parameters.Add("username", "admin");
      client.Parameters.Add("password", "mypassword");
      client.Post();
      string SessionKey = client.Response.result.ToString();
 
      client.ClearParameters();
 
      if(client.Response.StatusCode == System.Net.HttpStatusCode.OK){
        client.Method = "import_survey";
        client.Parameters.Add("sSessionKey", SessionKey);
        client.Parameters.Add("sImportData", Base64Encode(yourImportDataString));
        client.Parameters.Add("sImportDataType", "lss");
        //client.Parameters.Add("sNewSurveyName", "test");
        //client.Parameters.Add("DestSurveyID", 1);
        client.Post();
      }
 
      client.ClearParameters();
 
      Console.WriteLine("new survey id:" + client.Response.result.ToString());
      Console.ReadLine();
Last edit: 4 years 6 months ago by floho.
The following user(s) said Thank You: DenisChenu, KaiHilberger

Please Log in or Create an account to join the conversation.

Start now!

Just create your account and start using Limesurvey today.

Register now
Join our Newsletter!