# Mind RPC PartnerID x does not have access to operation offerSearch



## Eric2XU (Mar 18, 2016)

So I figured out how to write a node.js tls connection to the TiVo using this code:


```
var tls = require('tls');
var fs = require('fs');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var options = {
	host : "192.168.1.1"
	,rejectUnauthorized: false
	,port : 1413
	,pfx : fs.readFileSync('c:\\temp\\xxxxxxxx.p12')
	,passphrase : "xxxxxxxx"
	,ca : [fs.readFileSync('c:\\temp\\tivo.ca'), fs.readFileSync('c:\\temp\\tivo.int')]
	,secureProtocol: "TLSv1_1_method"
};

var sessionID = Math.floor(Math.random() * (2612256 - 2539520) + 2539520).toString(16)
var eol = "\r\n";


var header = "Type: request" + eol;
header = header + "RpcId: 1" + eol;
header = header + "SchemaVersion: 17" + eol;
header = header + "Content-Type: application/json" + eol;
header = header + "RequestType: bodyAuthenticate" + eol;
header = header + "ResponseCount: single" + eol;
header = header + "BodyId: " + eol;
header = header + "X-ApplicationName: Quicksilver" + eol;
header = header + "X-ApplicationVersion: 1.2" + eol;
header = header + "X-ApplicationSessionId: 0x" + sessionID + eol;
header = header + eol;

var body = '{"type":"bodyAuthenticate","credential":{"type":"makCredential","key":"0123456789"}}' + "\n"

var firstline = "MRPC/2 " + header.length + " " + body.length + eol


var client = this;
client.socket = tls.connect(options, function () {
	client.socket.setEncoding('utf8');
	client.socket.write(firstline + header + body);
	
	var data = "";
	
	client.socket.on('data', function (chunk) {
		data += chunk;
		console.log(data);
	});

	client.socket.on('end', function () {
		client.socket.end();
		
	});

	client.socket.on('error', function (err) {
		console.log("Error during TLS request");
		console.log(err);
		client.socket.end();
	});
	
});
```
Sure enough it responds with:

```
MRPC/2 75 119
Content-Type: application/json
IsFinal: true
RpcId: 1
Type: response

{"message": "Authentication successful", "status": "success", "type": "bodyAuthenticateResponse", "mediaAccessKey": ""}
```
However when I try to run this:

```
MRPC/2 230 75

Type: request
RpcId: 1
SchemaVersion: 17
Content-Type: application/json
RequestType: offerSearch
ResponseCount: single
BodyId:
X-ApplicationName: Quicksilver
X-ApplicationVersion: 1.2
X-ApplicationSessionId: 0x26f44a

{"type": "offerSearch","title": "The Daily Show","orderBy": ["startTime"]}
```
I get back: 

```
{"code": "routingError", "text": "PartnerId 3976 does not have access to operation offerSearch.", "type": "error"}
```
So my first question to anyone that has experience with MindRPC, does this error message mean that the IOS app doesnt have the rights to do a simple search? Or does it mean I am not stringing my first bodyAuthenticate correctly with my second query to request do the search.

Given its specific about a partnerID I am going to assume the IOS app cert I am using doesnt have rights to search. If that is the case, is there another way I can search for a show name? I noticed kmttg pulls the entire collection (which takes a long time), is that the only way to get the data?

Could someone help me with what command is needed to pull all data if that is what is needed (I will cache it locally every "x" mins if needed).

Finally while I have your attention, do you know the specific rpc command needed to start playing a specific show?

Thanks in advance!!


----------



## moyekj (Jan 24, 2006)

BodyId can't be empty for "offerSearch". Needs to be "tsn:xxxxxx"


----------



## Eric2XU (Mar 18, 2016)

I just tried sliding in the BodyID as tsn:xxxxx and still got the same message.

Here is what I sent (I slightly changed my TSN)


```
MRPC/2 249 75

Type: request
RpcId: 1
SchemaVersion: 17
Content-Type: application/json
RequestType: offerSearch
ResponseCount: single
BodyId: tsn:3480001501FAAB7
X-ApplicationName: Quicksilver
X-ApplicationVersion: 1.2
X-ApplicationSessionId: 0x26e30d

{"type": "offerSearch","title": "The Daily Show","orderBy": ["startTime"]}
```
And this is what I got back:


```
MRPC/2 75 114
Content-Type: application/json
IsFinal: true
RpcId: 1
Type: response

{"code": "routingError", "text": "PartnerId 3976 does not have access to operation offerSearch.", "type": "error"}
```
Any other ideas? Or is there some other command I can use to gather all shows and do you know the command to play a show by id?


----------



## moyekj (Jan 24, 2006)

To play a show on a local TiVo you need it's recordingId, so offerSearch isn't what you want anyway. You need to obtain list of shows on the TiVo using repeated "recordingFolderItemSearch" calls with offset incremented until you get no further results to get full list of shows:

```
{"flatten":true,"offset":0,"bodyId":"tsn:xxxx","type":"recordingFolderItemSearch"}
```
Then to play a show using it's recordingId:

```
{"type":"uiNavigate","uri":"x-tivo:classicui:playback","parameters":{"fUseTrioId":"true","fHideBannerOnEnter":"true","recordingId":"tivo:rc.xxxxxxxxx"}}
```
NOTE: Playing back a SKIP enabled show the above way prevents SkipMode from working. (Same problem exists if you start playback of a show from iOS/Android apps).

Also it looks like you're not incrementing RpcId with subsequent calls which I think you need to do.


----------



## Eric2XU (Mar 18, 2016)

thx moyekj, sadly same error, I guess I must be doing something wrong?

Here is what I submit:


```
MRPC/2 263 94

Type: request
RpcId: 3
SchemaVersion: 17
Content-Type: application/json
RequestType: recordingFolderItemSearch
ResponseCount: single
BodyId: tsn:6480001701FCC2
X-ApplicationName: Quicksilver
X-ApplicationVersion: 1.2
X-ApplicationSessionId: 0x27521a

{"flatten":true,"offset":0,"bodyId":"tsn:6480001701FCC2","type":"recordingFolderItemSearch"}
```
This is what I get back


```
MRPC/2 75 128
Content-Type: application/json
IsFinal: true
RpcId: 3
Type: response

{"code": "routingError", "text": "PartnerId 3976 does not have access to operation recordingFolderItemSearch.", "type": "error"}
```


----------



## moyekj (Jan 24, 2006)

Probably issue with certificate you are using then. Also note I sent you a PM on a somewhat unrelated matter.

Also, tsn 648 you list above is a series 3 TiVo which doesn't support direct RPC communication...


----------



## Eric2XU (Mar 18, 2016)

Just sent back the PM, I figured out I wasnt at the 10 required posts that were required to respond to a PM. 

I had changed my TSN to not post my real one publicly. Also I am using a Roamio. 

Do you know if its common to see that style of error message as a catch all message. Figuring out if it knows who I am (emulating a IOS app) or if I am just not saying the right things would be helpful to know which way to troubleshoot next.


----------



## Eric2XU (Mar 18, 2016)

Another update, made alot of progress. I was able to get past the partner x doesnt have rights. Sure enough I wasnt linking my bodyAuthenticate request with subsequent requests over the same socket.

For anyone that would like to interact with TiVo via a Node.js script I will post my semi-functional code below.

However my larger issue is now back to good old fashioned mis-understanding of the API.

I am able to run this:

```
{"flatten":false,"offset":0,"bodyId":"tsn:xxxxxxxxxxx","type":"recordingFolderItemSearch"}
```
_Note: I set flatten to false because setting to true is 1) slow and 2) returns malformed JSON which I can not parse effectively with built in node.js libraries. This shouldn't be an issue though, I am able to get the show's parent folder ID and could go back to the TiVo to get a listing of recordings specific to that folder ID. _

Sure enough I get back all the parent folder ID's. So for example one of my parent folders looks like this:

```
{ bodyId: 'tsn:xxxxxxxxxxxxxx',
       childRecordingId: 'tivo:rc.109822539',
       recordingFolderItemId: 'tivo:rf.109822539',
       startTime: '2016-03-31 04:01:00',
       title: 'At Midnight With Chris Hardwick',
       newItems: 1,
       collectionId: 'tivo:cl.334914484',
       collectionType: 'series',
       transportType: 'stream',
       contentId: 'tivo:ct.341991971',
       subscriptionIdentifier: [Object],
       isAdult: false,
       recordingStreamProhibited: false,
       recordingTransferProhibited: false,
       percentWatched: 0,
       type: 'recordingFolderItem' },
```
Next I try to go back to the TiVo to get a listing of each shows on the DVR with that recordingFolderItemId or childRecordingId (they are the same ID). This is where I am failing.

Using this:

```
{"orderBy": ["startTime"],"type": "recordingFolderItemSearch","bodyId": "tsn:xxxxxxxxxxxxx","note": ["recordingForChildRecordingId"],"parentRecordingFolderItemId": "tivo:rf.109822539"}
```
All I get back is: 

```
{ type: 'recordingFolderItemList' }
```
So theres a bit of "now what". I inch closer but yet seem so far away puzzled by why TiVo's api is so barbaric. Going to see if anyone out there has any ideas because my next move is to manually parse the all shows JSON which is going to be very sloppy and slow.

---------------------
Node.js Code for talking to the TiVo:

```
var tls = require('tls');
var fs = require('fs');
var async = require('async');

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var options = {
	host : "192.168.1.1"
	,rejectUnauthorized: false
	,port : 1413
	,pfx : fs.readFileSync('c:\\temp\\xxxxxx.p12')
	,passphrase : "xxxxxx"
	,ca : [fs.readFileSync('c:\\temp\\tivo.ca'), fs.readFileSync('c:\\temp\\tivo.int')]
	,secureProtocol: "TLSv1_1_method"
};

var sessionID = Math.floor(Math.random() * (2612256 - 2539520) + 2539520).toString(16)
var eol = "\r\n";
var RpcId = 0

function buildPlayload(type,body) {
	RpcId++
	var header = "Type: request" + eol;
	header = header + "RpcId: " + RpcId + eol;
	header = header + "SchemaVersion: 17" + eol;
	header = header + "Content-Type: application/json" + eol;
	header = header + "RequestType: " + type + eol;
	header = header + "ResponseCount: single" + eol;
	header = header + "BodyId: " + eol;
	header = header + "X-ApplicationName: Quicksilver" + eol;
	header = header + "X-ApplicationVersion: 1.2" + eol;
	header = header + "X-ApplicationSessionId: 0x" + sessionID + eol;
	header = header + eol;

	var bodyline = body + "\n"

	var firstline = "MRPC/2 " + header.length + " " + bodyline.length + eol

	return firstline + header + bodyline
}


function RPCRequest(payloadData, callback) {
	var client = this;
	var responseData = "";
	var bodyAuth = buildPlayload('bodyAuthenticate', '{"type":"bodyAuthenticate","credential":{"type":"makCredential","key":"4685330376"}}');
	var payload = buildPlayload(payloadData[0], payloadData[1])
	
	client.socket = tls.connect(options, function () {
		client.socket.setEncoding('utf8');
		client.socket.write(bodyAuth);
		setTimeout(function () { client.socket.write(payload); }, 300);
		
		client.socket.on('data', function (chunk) {
			callback(chunk);	
		});
		
		client.socket.on('end', function () {
			console.log("--------CONNECTION ENDED-----------")
		});
		
		client.socket.on('error', function (err) {
			console.log("Error during TLS request");
			console.log(err);
			client.socket.end();
		});
	
	});
}

function parseTiVosMess(dataStream) { 
	headerInfo = dataStream.split("\r\n", 1).toString()
	version = (headerInfo.split(" ", 3))[0]
	headerSize = parseInt((headerInfo.split(" ", 3))[1]) + headerInfo.length
	bodySize = (headerInfo.split(" ", 3))[2]
	body = JSON.parse(dataStream.substring(parseInt(headerSize), dataStream.length).trim());
	return body
}


// Examples
//var tivoReq = ['bodyAuthenticate', '{"type":"bodyAuthenticate","credential":{"type":"makCredential","key":"4685330376"}}']
//var tivoReq = ['bodyConfigSearch', '{ "type": "bodyConfigSearch", "bodyId": ""}']
//var tivoReq = ['recordingFolderItemSearch', '{"flatten":true,"offset":0,"bodyId":"tsn:xxxxxxxxxx","type":"recordingFolderItemSearch"}']
//var tivoReq = ['offerSearch', '{"bodyId":"tsn:xxxxxxxxxx","type": "offerSearch","title": "The Daily Show","orderBy": ["startTime"]}']


///////////////////////////////
////
//// TiVo Command Next Line
////
/////////////////////////////////

var tivoReq = ['recordingFolderItemSearch', '{"orderBy": ["startTime"],"type": "recordingFolderItemSearch","bodyId": "tsn:xxxxxxxxxx","note": ["recordingForChildRecordingId"],"parentRecordingFolderItemId": "tivo:rf.109822539"}']
RPCRequest(tivoReq, function (daData) {
	myJSON = parseTiVosMess(daData);
	console.dir(myJSON);
})
```


----------

